summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÁlvaro Herrera <alvherre@kurilemu.de>2025-10-11 20:30:12 +0200
committerÁlvaro Herrera <alvherre@kurilemu.de>2025-10-11 20:30:12 +0200
commit3231fd0455220194d6f75323c83819a38fc0a382 (patch)
treecf19bee37e887c709d66e5e3779a4a8213fbd9a2
parentff47f9c16c2f626d828a473d63c90d03f18a34a3 (diff)
Stop creating constraints during DETACH CONCURRENTLY
Commit 71f4c8c6f74b (which implemented DETACH CONCURRENTLY) added code to create a separate table constraint when a table is detached concurrently, identical to the partition constraint, on the theory that such a constraint was needed in case the optimizer had constructed any query plans that depended on the constraint being there. However, that theory was apparently bogus because any such plans would be invalidated. For hash partitioning, those constraints are problematic, because their expressions reference the OID of the parent partitioned table, to which the detached table is no longer related; this causes all sorts of problems (such as inability of restoring a pg_dump of that table, and the table no longer working properly if the partitioned table is later dropped). We'd like to get rid of all those constraints. In fact, for branch master, do that -- no longer create any substitute constraints. However, out of fear that some users might somehow depend on these constraints for other partitioning strategies, for stable branches (back to 14, which added DETACH CONCURRENTLY), only do it for hash partitioning. (If you repeatedly DETACH CONCURRENTLY and then ATTACH a partition, then with this constraint addition you don't need to scan the table in the ATTACH step, which presumably is good. But if users really valued this feature, they would have requested that it worked for non-concurrent DETACH also.) Author: Haiyang Li <mohen.lhy@alibaba-inc.com> Reported-by: Fei Changhong <feichanghong@qq.com> Reported-by: Haiyang Li <mohen.lhy@alibaba-inc.com> Backpatch-through: 14 Discussion: https://postgr.es/m/18371-7fef49f63de13f02@postgresql.org Discussion: https://postgr.es/m/19070-781326347ade7c57@postgresql.org
-rw-r--r--src/backend/commands/tablecmds.c50
-rw-r--r--src/test/regress/expected/alter_table.out28
-rw-r--r--src/test/regress/sql/alter_table.sql15
3 files changed, 17 insertions, 76 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fc89352b661..5fd8b51312c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -722,7 +722,6 @@ static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
List *partConstraint,
bool validate_default);
static void CloneRowTriggersToPartition(Relation parent, Relation partition);
-static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
static void DropClonedTriggersFromPartition(Oid partitionId);
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
Relation rel, RangeVar *name,
@@ -20942,12 +20941,6 @@ ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
char *partrelname;
/*
- * Add a new constraint to the partition being detached, which
- * supplants the partition constraint (unless there is one already).
- */
- DetachAddConstraintIfNeeded(wqueue, partRel);
-
- /*
* We're almost done now; the only traces that remain are the
* pg_inherits tuple and the partition's relpartbounds. Before we can
* remove those, we need to wait until all transactions that know that
@@ -21404,49 +21397,6 @@ ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
}
/*
- * DetachAddConstraintIfNeeded
- * Subroutine for ATExecDetachPartition. Create a constraint that
- * takes the place of the partition constraint, but avoid creating
- * a dupe if a constraint already exists which implies the needed
- * constraint.
- */
-static void
-DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
-{
- List *constraintExpr;
-
- constraintExpr = RelationGetPartitionQual(partRel);
- constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
-
- /*
- * Avoid adding a new constraint if the needed constraint is implied by an
- * existing constraint
- */
- if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
- {
- AlteredTableInfo *tab;
- Constraint *n;
-
- tab = ATGetQueueEntry(wqueue, partRel);
-
- /* Add constraint on partition, equivalent to the partition constraint */
- n = makeNode(Constraint);
- n->contype = CONSTR_CHECK;
- n->conname = NULL;
- n->location = -1;
- n->is_no_inherit = false;
- n->raw_expr = NULL;
- n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
- n->is_enforced = true;
- n->initially_valid = true;
- n->skip_validation = true;
- /* It's a re-add, since it nominally already exists */
- ATAddCheckNNConstraint(wqueue, tab, partRel, n,
- true, false, true, ShareUpdateExclusiveLock);
- }
-}
-
-/*
* DropClonedTriggersFromPartition
* subroutine for ATExecDetachPartition to remove any triggers that were
* cloned to the partition when it was created-as-partition or attached.
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index a08f115b0e5..5e98bbf2425 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -4470,27 +4470,15 @@ ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY;
Partition key: RANGE (a)
Number of partitions: 0
--- constraint should be created
-\d part_rp
- Table "public.part_rp"
- Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+---------
- a | integer | | |
-Check constraints:
- "part_rp_a_check" CHECK (a IS NOT NULL AND a >= 0 AND a < 100)
-
-CREATE TABLE part_rp100 PARTITION OF range_parted2 (CHECK (a>=123 AND a<133 AND a IS NOT NULL)) FOR VALUES FROM (100) to (200);
-ALTER TABLE range_parted2 DETACH PARTITION part_rp100 CONCURRENTLY;
--- redundant constraint should not be created
-\d part_rp100
- Table "public.part_rp100"
- Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+---------
- a | integer | | |
-Check constraints:
- "part_rp100_a_check" CHECK (a >= 123 AND a < 133 AND a IS NOT NULL)
-
DROP TABLE range_parted2;
+-- Test that hash partitions continue to work after they're concurrently
+-- detached (bugs #18371, #19070)
+CREATE TABLE hash_parted2 (a int) PARTITION BY HASH(a);
+CREATE TABLE part_hp PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 2, REMAINDER 0);
+ALTER TABLE hash_parted2 DETACH PARTITION part_hp CONCURRENTLY;
+DROP TABLE hash_parted2;
+INSERT INTO part_hp VALUES (1);
+DROP TABLE part_hp;
-- Check ALTER TABLE commands for partitioned tables and partitions
-- cannot add/drop column to/from *only* the parent
ALTER TABLE ONLY list_parted2 ADD COLUMN c int;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 90bf5c17682..417202430a5 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2826,14 +2826,17 @@ DROP TABLE part_rpd;
-- works fine
ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY;
\d+ range_parted2
--- constraint should be created
-\d part_rp
-CREATE TABLE part_rp100 PARTITION OF range_parted2 (CHECK (a>=123 AND a<133 AND a IS NOT NULL)) FOR VALUES FROM (100) to (200);
-ALTER TABLE range_parted2 DETACH PARTITION part_rp100 CONCURRENTLY;
--- redundant constraint should not be created
-\d part_rp100
DROP TABLE range_parted2;
+-- Test that hash partitions continue to work after they're concurrently
+-- detached (bugs #18371, #19070)
+CREATE TABLE hash_parted2 (a int) PARTITION BY HASH(a);
+CREATE TABLE part_hp PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 2, REMAINDER 0);
+ALTER TABLE hash_parted2 DETACH PARTITION part_hp CONCURRENTLY;
+DROP TABLE hash_parted2;
+INSERT INTO part_hp VALUES (1);
+DROP TABLE part_hp;
+
-- Check ALTER TABLE commands for partitioned tables and partitions
-- cannot add/drop column to/from *only* the parent