agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH v2a 1/4] Add ternary reloption type 195+ messages / 2 participants [nested] [flat]
* [PATCH v2a 1/4] Add ternary reloption type @ 2025-09-04 14:34 Nikolay Shaplov <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Nikolay Shaplov @ 2025-09-04 14:34 UTC (permalink / raw) There is a tendency for boolean reloptions in PostgreSQL code, one with "on" and "off" values, to be replaced with options with "on", "off", "[use_global_settings]" behaviour. For `vacuum_index_cleanup" and gist's `buffering` reloption such behavior have been implemented as enum-tyoe option. For vacuum_truncate this behaviour have been umplemented by adding additional `is_set` flag to `bytea` representation of the reloptions. Both solutions looks like workaround hacks to implement option with three available states. This patch introduce "ternary" reloption type, that behave like bool option, but also has an additional "unset" state. This state may be reachable only by not setting or RESETting an option, like in `vacuum_truncate` option, or it may have some text alias that allow user to explicitly set it, like "auto" in `vacuum_index_cleanup` or gist's `buffering` `vacuum_truncate`, `vacuum_index_cleanup` and gist's `buffering` reloptions are reimplemented as ternary reloptions without significant behaviour changes. --- This patch is split into four parts, to help reviewer grasp the login behind it. I guess it is better to commit it as a single commit --- Part1: Add regression tests that would be tests for ternary reloptions in future, but now it checks the behaviour of reloptions that would become ternary. There behaviour should not change, so adding tests before changing anything. These tests should pass before applying the patch, and after it. --- src/test/regress/expected/reloptions.out | 36 ++++++++++++++++++++++++ src/test/regress/sql/reloptions.sql | 21 ++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out index 9de19b4e3f..1c99f79ab0 100644 --- a/src/test/regress/expected/reloptions.out +++ b/src/test/regress/expected/reloptions.out @@ -98,6 +98,42 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; {fillfactor=13,autovacuum_enabled=false} (1 row) +-- Tests for future (FIXME) ternary options +-- behave as boolean option: accept unassigned name and truncated value +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT) WITH (vacuum_truncate); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; + reloptions +------------------------ + {vacuum_truncate=true} +(1 row) + +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT) WITH (vacuum_truncate=FaLS); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; + reloptions +------------------------ + {vacuum_truncate=fals} +(1 row) + +-- preferred "true" alias is used when storing +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT) WITH (vacuum_index_cleanup=on); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; + reloptions +--------------------------- + {vacuum_index_cleanup=on} +(1 row) + +-- custom "third" value is available +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT) WITH (vacuum_index_cleanup=auto); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; + reloptions +----------------------------- + {vacuum_index_cleanup=auto} +(1 row) + -- Test vacuum_truncate option DROP TABLE reloptions_test; CREATE TEMP TABLE reloptions_test(i INT NOT NULL, j text) diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql index 24fbe0b478..f5980dafcb 100644 --- a/src/test/regress/sql/reloptions.sql +++ b/src/test/regress/sql/reloptions.sql @@ -59,6 +59,27 @@ UPDATE pg_class ALTER TABLE reloptions_test RESET (illegal_option); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; +-- Tests for future (FIXME) ternary options + +-- behave as boolean option: accept unassigned name and truncated value +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT) WITH (vacuum_truncate); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; + +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT) WITH (vacuum_truncate=FaLS); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; + +-- preferred "true" alias is used when storing +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT) WITH (vacuum_index_cleanup=on); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; + +-- custom "third" value is available +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT) WITH (vacuum_index_cleanup=auto); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; + -- Test vacuum_truncate option DROP TABLE reloptions_test; -- 2.39.2 --nextPart4155600.3ZeAukHxDK Content-Disposition: attachment; filename="v2a-0002-Introduce-ternary-reloptions.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="unicode-2-0-utf-8"; name="v2a-0002-Introduce-ternary-reloptions.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
* [PATCH v50 5/8] support repacking tables with exclusion constraints @ 2026-04-02 18:59 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 195+ messages in thread From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw) --- src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 73eadd1ff4a..03829892d57 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -49,6 +49,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_am.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_control.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" @@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea TransactionId frozenXid, MultiXactId cutoffMulti); static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes); +static void copy_index_constraints(Relation old_index, Oid new_index_id, + Oid new_heap_id); static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, @@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) false); newindex = index_create_copy(NewHeap, false, oldindex, ind->rd_rel->reltablespace, newName); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); index_close(ind, NoLock); @@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) return result; } +/* + * Create a transient copy of a constraint -- supported by a transient + * copy of the index that supports the original constraint. + * + * When repacking a table that contains exclusion constraints, the executor + * relies on these constraints being properly catalogued. These copies are + * to support that. + * + * We don't need the constraints for anything else (the original constraints + * will be there once repack completes), so we add pg_depend entries so that + * the are dropped when the transient table is dropped. + */ +static void +copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) +{ + ScanKeyData skey; + Relation rel; + TupleDesc desc; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objrel; + + rel = table_open(ConstraintRelationId, RowExclusiveLock); + ObjectAddressSet(objrel, RelationRelationId, new_heap_id); + + /* + * Retrieve the constraints supported by the old index and create an + * identical one that points to the new index. + */ + ScanKeyInit(&skey, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(old_index->rd_index->indrelid)); + scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, + NULL, 1, &skey); + desc = RelationGetDescr(rel); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + Oid oid; + Datum values[Natts_pg_constraint] = {0}; + bool nulls[Natts_pg_constraint] = {0}; + bool replaces[Natts_pg_constraint] = {0}; + HeapTuple new_tup; + ObjectAddress objcon; + + if (conform->conindid != RelationGetRelid(old_index)) + continue; + + oid = GetNewOidWithIndex(rel, ConstraintOidIndexId, + Anum_pg_constraint_oid); + values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid); + replaces[Anum_pg_constraint_oid - 1] = true; + values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id); + replaces[Anum_pg_constraint_conrelid - 1] = true; + values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id); + replaces[Anum_pg_constraint_conindid - 1] = true; + + new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces); + + /* Insert it into the catalog. */ + CatalogTupleInsert(rel, new_tup); + + /* Create a dependency so it's removed when we drop the new heap. */ + ObjectAddressSet(objcon, ConstraintRelationId, oid); + recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO); + } + systable_endscan(scan); + + table_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); +} + /* * Try to start a background worker to perform logical decoding of data * changes applied to relation while REPACK CONCURRENTLY is copying its -- 2.47.3 --brnevsqjnuzpyok4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch" ^ permalink raw reply [nested|flat] 195+ messages in thread
end of thread, other threads:[~2026-04-02 18:59 UTC | newest] Thread overview: 195+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2025-09-04 14:34 [PATCH v2a 1/4] Add ternary reloption type Nikolay Shaplov <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]> 2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox