agora inbox for pgsql-hackers@postgresql.orghelp / color / mirror / Atom feed
[PATCH v4 5/6] Handle pg_get_expr default args in system_functions.sql 355+ messages / 2 participants [nested] [flat]
* [PATCH v4 5/6] Handle pg_get_expr default args in system_functions.sql @ 2025-12-09 19:17 Mark Wong <markwkm@gmail.com> 0 siblings, 0 replies; 355+ messages in thread From: Mark Wong @ 2025-12-09 19:17 UTC (permalink / raw) Modernize pg_get_expr to use proargdefaults to handle the optional pretty argument. That also means any direct function calls now needs to set the pretty parameter. --- src/backend/commands/tablecmds.c | 5 +++-- src/backend/utils/adt/ruleutils.c | 17 ----------------- src/include/catalog/pg_proc.dat | 7 +++---- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index b04b0dbd2a0..a523a512949 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -17477,8 +17477,9 @@ decompile_conbin(HeapTuple contup, TupleDesc tupdesc) if (isnull) elog(ERROR, "null conbin for constraint %u", con->oid); - expr = DirectFunctionCall2(pg_get_expr, attr, - ObjectIdGetDatum(con->conrelid)); + expr = DirectFunctionCall3(pg_get_expr, attr, + ObjectIdGetDatum(con->conrelid), + BoolGetDatum(false)); return TextDatumGetCString(expr); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 03550f0d80b..a70807e84ca 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2574,23 +2574,6 @@ decompile_column_index_array(Datum column_index_array, Oid relId, */ Datum pg_get_expr(PG_FUNCTION_ARGS) -{ - text *expr = PG_GETARG_TEXT_PP(0); - Oid relid = PG_GETARG_OID(1); - text *result; - int prettyFlags; - - prettyFlags = PRETTYFLAG_INDENT; - - result = pg_get_expr_worker(expr, relid, prettyFlags); - if (result) - PG_RETURN_TEXT_P(result); - else - PG_RETURN_NULL(); -} - -Datum -pg_get_expr_ext(PG_FUNCTION_ARGS) { text *expr = PG_GETARG_TEXT_PP(0); Oid relid = PG_GETARG_OID(1); diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 1d1b24a8f4c..69c1e5bb264 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -3987,9 +3987,6 @@ { oid => '1662', descr => 'trigger description', proname => 'pg_get_triggerdef', provolatile => 's', prorettype => 'text', proargtypes => 'oid', prosrc => 'pg_get_triggerdef' }, -{ oid => '1716', descr => 'deparse an encoded expression', - proname => 'pg_get_expr', provolatile => 's', prorettype => 'text', - proargtypes => 'pg_node_tree oid', prosrc => 'pg_get_expr' }, { oid => '1665', descr => 'name of sequence for a serial column', proname => 'pg_get_serial_sequence', provolatile => 's', prorettype => 'text', proargtypes => 'text text', prosrc => 'pg_get_serial_sequence' }, @@ -8531,7 +8528,9 @@ { oid => '2509', descr => 'deparse an encoded expression with pretty-print option', proname => 'pg_get_expr', provolatile => 's', prorettype => 'text', - proargtypes => 'pg_node_tree oid bool', prosrc => 'pg_get_expr_ext' }, + proargtypes => 'pg_node_tree oid bool', + proargnames => '{expr,relation,pretty}', proargdefaults => '{false}', + prosrc => 'pg_get_expr' }, { oid => '2510', descr => 'get the prepared statements for this session', proname => 'pg_prepared_statement', prorows => '1000', proretset => 't', provolatile => 's', proparallel => 'r', prorettype => 'record', -- 2.43.0 --eEEy3EHc57lA8spa Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v4-0006-Handle-pg_get_triggerdef-default-args-in-system_f.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 355+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 355+ messages in thread
end of thread, other threads:[~2026-04-02 18:59 UTC | newest] Thread overview: 355+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2025-12-09 19:17 [PATCH v4 5/6] Handle pg_get_expr default args in system_functions.sql Mark Wong <markwkm@gmail.com> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox