diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 7dcd634a1a..fd67d2a841 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -15682,6 +15682,13 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) defaultrel = table_open(defaultPartOid, NoLock); defPartConstraint = get_proposed_default_constraint(partBoundConstraint); + /* + * Map the Vars in the constraint expression from rel's attnos to + * defaultrel's. + */ + defPartConstraint = + map_partition_varattnos(defPartConstraint, + 1, defaultrel, rel, NULL); QueuePartitionConstraintValidation(wqueue, defaultrel, defPartConstraint, true); diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index 99d26de7e6..f3c9236ad5 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -1237,6 +1237,13 @@ check_default_partition_contents(Relation parent, Relation default_rel, : get_qual_for_range(parent, new_spec, false); def_part_constraints = get_proposed_default_constraint(new_part_constraints); + /* + * Map the Vars in the constraint expression from parent's attnos to + * default_rel's. + */ + def_part_constraints = + map_partition_varattnos(def_part_constraints, 1, default_rel, + parent, NULL); /* * If the existing constraints on the default partition imply that it will @@ -1265,7 +1272,6 @@ check_default_partition_contents(Relation parent, Relation default_rel, { Oid part_relid = lfirst_oid(lc); Relation part_rel; - Expr *constr; Expr *partition_constraint; EState *estate; ExprState *partqualstate = NULL; @@ -1280,6 +1286,15 @@ check_default_partition_contents(Relation parent, Relation default_rel, { part_rel = table_open(part_relid, NoLock); + /* + * Map the Vars in the constraint expression from default_rel's + * the sub-partition's. + */ + partition_constraint = make_ands_explicit(def_part_constraints); + partition_constraint = (Expr *) + map_partition_varattnos((List *) partition_constraint, 1, + part_rel, default_rel, NULL); + /* * If the partition constraints on default partition child imply * that it will not contain any row that would belong to the new @@ -1297,7 +1312,10 @@ check_default_partition_contents(Relation parent, Relation default_rel, } } else + { part_rel = default_rel; + partition_constraint = make_ands_explicit(def_part_constraints); + } /* * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be @@ -1318,10 +1336,6 @@ check_default_partition_contents(Relation parent, Relation default_rel, continue; } - constr = linitial(def_part_constraints); - partition_constraint = (Expr *) - map_partition_varattnos((List *) constr, - 1, part_rel, parent, NULL); estate = CreateExecutorState(); /* Build expression execution states for partition check quals */ diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index a639601f66..e687150511 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -4008,7 +4008,8 @@ DROP USER regress_alter_table_user1; -- default partition create table defpart_attach_test (a int) partition by list (a); create table defpart_attach_test1 partition of defpart_attach_test for values in (1); -create table defpart_attach_test_d (like defpart_attach_test); +create table defpart_attach_test_d (b int, a int); +alter table defpart_attach_test_d drop b; insert into defpart_attach_test_d values (1), (2); -- error because its constraint as the default partition would be violated -- by the row containing 1 @@ -4019,6 +4020,12 @@ alter table defpart_attach_test_d add check (a > 1); -- should be attached successfully and without needing to be scanned alter table defpart_attach_test attach partition defpart_attach_test_d default; INFO: partition constraint for table "defpart_attach_test_d" is implied by existing constraints +-- check that attaching a partition correctly reports any rows in the default +-- partition that should not be there for the new partition to be attached +-- successfully +create table defpart_attach_test_2 (like defpart_attach_test_d); +alter table defpart_attach_test attach partition defpart_attach_test_2 for values in (2); +ERROR: updated partition constraint for default partition would be violated by some row drop table defpart_attach_test; -- check combinations of temporary and permanent relations when attaching -- partitions. diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 204441e759..262abf2bfb 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -1119,3 +1119,17 @@ select tableoid::regclass from volatile_partbound_test; (1 row) drop table volatile_partbound_test; +-- test the case where a check constraint on default partition allows +-- to avoid scanning it when adding a new partition +create table defcheck (a int, b int) partition by list (b); +create table defcheck_def (a int, c int, b int); +alter table defcheck_def drop c; +alter table defcheck attach partition defcheck_def default; +alter table defcheck_def add check (b <= 0 and b is not null); +create table defcheck_1 partition of defcheck for values in (1, null); +INFO: updated partition constraint for default partition "defcheck_def" is implied by existing constraints +-- test that complex default partition constraints are enforced correctly +insert into defcheck_def values (0, 0); +create table defcheck_0 partition of defcheck for values in (0); +ERROR: updated partition constraint for default partition "defcheck_def" would be violated by some row +drop table defcheck; diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 0369909b50..8016f8a823 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -2648,7 +2648,8 @@ DROP USER regress_alter_table_user1; -- default partition create table defpart_attach_test (a int) partition by list (a); create table defpart_attach_test1 partition of defpart_attach_test for values in (1); -create table defpart_attach_test_d (like defpart_attach_test); +create table defpart_attach_test_d (b int, a int); +alter table defpart_attach_test_d drop b; insert into defpart_attach_test_d values (1), (2); -- error because its constraint as the default partition would be violated @@ -2660,6 +2661,12 @@ alter table defpart_attach_test_d add check (a > 1); -- should be attached successfully and without needing to be scanned alter table defpart_attach_test attach partition defpart_attach_test_d default; +-- check that attaching a partition correctly reports any rows in the default +-- partition that should not be there for the new partition to be attached +-- successfully +create table defpart_attach_test_2 (like defpart_attach_test_d); +alter table defpart_attach_test attach partition defpart_attach_test_2 for values in (2); + drop table defpart_attach_test; -- check combinations of temporary and permanent relations when attaching diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 751c0d39f5..9c6d86a0bf 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -856,3 +856,17 @@ create table volatile_partbound_test2 partition of volatile_partbound_test for v insert into volatile_partbound_test values (current_timestamp); select tableoid::regclass from volatile_partbound_test; drop table volatile_partbound_test; + +-- test the case where a check constraint on default partition allows +-- to avoid scanning it when adding a new partition +create table defcheck (a int, b int) partition by list (b); +create table defcheck_def (a int, c int, b int); +alter table defcheck_def drop c; +alter table defcheck attach partition defcheck_def default; +alter table defcheck_def add check (b <= 0 and b is not null); +create table defcheck_1 partition of defcheck for values in (1, null); + +-- test that complex default partition constraints are enforced correctly +insert into defcheck_def values (0, 0); +create table defcheck_0 partition of defcheck for values in (0); +drop table defcheck;