Don't add a redundant constraint when detaching a partition

On ALTER TABLE .. DETACH CONCURRENTLY, we add a new table constraint
that duplicates the partition constraint.  But if the partition already
has another constraint that implies that one, then that's unnecessary.
We were already avoiding the addition of a duplicate constraint if there
was an exact 'equal' match -- this just improves the quality of the check.

Author: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Álvaro Herrera <alvherre@alvh.no-ip.org>
Discussion: https://postgr.es/m/20210410184226.GY6592@telsasoft.com
This commit is contained in:
Alvaro Herrera 2021-04-21 18:12:05 -04:00
parent e014d25dea
commit 7b357cc6ae
No known key found for this signature in database
GPG Key ID: 1C20ACB9D5C564AE
3 changed files with 52 additions and 31 deletions

View File

@ -17915,47 +17915,42 @@ ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
* DetachAddConstraintIfNeeded * DetachAddConstraintIfNeeded
* Subroutine for ATExecDetachPartition. Create a constraint that * Subroutine for ATExecDetachPartition. Create a constraint that
* takes the place of the partition constraint, but avoid creating * takes the place of the partition constraint, but avoid creating
* a dupe if an equivalent constraint already exists. * a dupe if an constraint already exists which implies the needed
* constraint.
*/ */
static void static void
DetachAddConstraintIfNeeded(List **wqueue, Relation partRel) DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
{ {
AlteredTableInfo *tab; List *constraintExpr;
Expr *constraintExpr;
TupleDesc td = RelationGetDescr(partRel);
Constraint *n;
constraintExpr = make_ands_explicit(RelationGetPartitionQual(partRel)); constraintExpr = RelationGetPartitionQual(partRel);
constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
/* If an identical constraint exists, we don't need to create one */ /*
if (td->constr && td->constr->num_check > 0) * Avoid adding a new constraint if the needed constraint is implied by an
* existing constraint
*/
if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
{ {
for (int i = 0; i < td->constr->num_check; i++) AlteredTableInfo *tab;
{ Constraint *n;
Node *thisconstr;
thisconstr = stringToNode(td->constr->check[i].ccbin); tab = ATGetQueueEntry(wqueue, partRel);
if (equal(constraintExpr, thisconstr)) /* Add constraint on partition, equivalent to the partition constraint */
return; 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->initially_valid = true;
n->skip_validation = true;
/* It's a re-add, since it nominally already exists */
ATAddCheckConstraint(wqueue, tab, partRel, n,
true, false, true, ShareUpdateExclusiveLock);
} }
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(constraintExpr);
n->initially_valid = true;
n->skip_validation = true;
/* It's a re-add, since it nominally already exists */
ATAddCheckConstraint(wqueue, tab, partRel, n,
true, false, true, ShareUpdateExclusiveLock);
} }
/* /*

View File

@ -4191,6 +4191,26 @@ ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY;
Partition key: RANGE (a) Partition key: RANGE (a)
Number of partitions: 0 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; DROP TABLE range_parted2;
-- Check ALTER TABLE commands for partitioned tables and partitions -- Check ALTER TABLE commands for partitioned tables and partitions
-- cannot add/drop column to/from *only* the parent -- cannot add/drop column to/from *only* the parent

View File

@ -2696,6 +2696,12 @@ DROP TABLE part_rpd;
-- works fine -- works fine
ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY; ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY;
\d+ range_parted2 \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; DROP TABLE range_parted2;
-- Check ALTER TABLE commands for partitioned tables and partitions -- Check ALTER TABLE commands for partitioned tables and partitions