Skip to content

Commit 0563a3a

Browse files
committed
Fix a bug in how we generate partition constraints.
Move the code for doing parent attnos to child attnos mapping for Vars in partition constraint expressions to a separate function map_partition_varattnos() and call it from the appropriate places. Doing it in get_qual_from_partbound(), as is now, would produce wrong result in certain multi-level partitioning cases, because it only considers the current pair of parent-child relations. In certain multi-level partitioning cases, attnums for the same key attribute(s) might differ between various levels causing the same attribute to be numbered differently in different instances of the Var corresponding to a given attribute. With this commit, in generate_partition_qual(), we first generate the the whole partition constraint (considering all levels of partitioning) and then do the mapping, so that Vars in the final expression are numbered according the leaf relation (to which it is supposed to apply). Amit Langote, reviewed by me.
1 parent 0c2070c commit 0563a3a

File tree

5 files changed

+110
-52
lines changed

5 files changed

+110
-52
lines changed

src/backend/catalog/partition.c

+49-50
Original file line numberDiff line numberDiff line change
@@ -852,10 +852,6 @@ get_qual_from_partbound(Relation rel, Relation parent, Node *bound)
852852
PartitionBoundSpec *spec = (PartitionBoundSpec *) bound;
853853
PartitionKey key = RelationGetPartitionKey(parent);
854854
List *my_qual = NIL;
855-
TupleDesc parent_tupdesc = RelationGetDescr(parent);
856-
AttrNumber parent_attno;
857-
AttrNumber *partition_attnos;
858-
bool found_whole_row;
859855

860856
Assert(key != NULL);
861857

@@ -876,38 +872,51 @@ get_qual_from_partbound(Relation rel, Relation parent, Node *bound)
876872
(int) key->strategy);
877873
}
878874

879-
/*
880-
* Translate vars in the generated expression to have correct attnos. Note
881-
* that the vars in my_qual bear attnos dictated by key which carries
882-
* physical attnos of the parent. We must allow for a case where physical
883-
* attnos of a partition can be different from the parent.
884-
*/
885-
partition_attnos = (AttrNumber *)
886-
palloc0(parent_tupdesc->natts * sizeof(AttrNumber));
887-
for (parent_attno = 1; parent_attno <= parent_tupdesc->natts;
888-
parent_attno++)
875+
return my_qual;
876+
}
877+
878+
/*
879+
* map_partition_varattnos - maps varattno of any Vars in expr from the
880+
* parent attno to partition attno.
881+
*
882+
* We must allow for a case where physical attnos of a partition can be
883+
* different from the parent's.
884+
*/
885+
List *
886+
map_partition_varattnos(List *expr, Relation partrel, Relation parent)
887+
{
888+
TupleDesc tupdesc = RelationGetDescr(parent);
889+
AttrNumber attno;
890+
AttrNumber *part_attnos;
891+
bool found_whole_row;
892+
893+
if (expr == NIL)
894+
return NIL;
895+
896+
part_attnos = (AttrNumber *) palloc0(tupdesc->natts * sizeof(AttrNumber));
897+
for (attno = 1; attno <= tupdesc->natts; attno++)
889898
{
890-
Form_pg_attribute attribute = parent_tupdesc->attrs[parent_attno - 1];
899+
Form_pg_attribute attribute = tupdesc->attrs[attno - 1];
891900
char *attname = NameStr(attribute->attname);
892-
AttrNumber partition_attno;
901+
AttrNumber part_attno;
893902

894903
if (attribute->attisdropped)
895904
continue;
896905

897-
partition_attno = get_attnum(RelationGetRelid(rel), attname);
898-
partition_attnos[parent_attno - 1] = partition_attno;
906+
part_attno = get_attnum(RelationGetRelid(partrel), attname);
907+
part_attnos[attno - 1] = part_attno;
899908
}
900909

901-
my_qual = (List *) map_variable_attnos((Node *) my_qual,
902-
1, 0,
903-
partition_attnos,
904-
parent_tupdesc->natts,
905-
&found_whole_row);
906-
/* there can never be a whole-row reference here */
910+
expr = (List *) map_variable_attnos((Node *) expr,
911+
1, 0,
912+
part_attnos,
913+
tupdesc->natts,
914+
&found_whole_row);
915+
/* There can never be a whole-row reference here */
907916
if (found_whole_row)
908917
elog(ERROR, "unexpected whole-row reference found in partition key");
909918

910-
return my_qual;
919+
return expr;
911920
}
912921

913922
/*
@@ -1496,27 +1505,15 @@ generate_partition_qual(Relation rel)
14961505
/* Guard against stack overflow due to overly deep partition tree */
14971506
check_stack_depth();
14981507

1508+
/* Quick copy */
1509+
if (rel->rd_partcheck != NIL)
1510+
return copyObject(rel->rd_partcheck);
1511+
14991512
/* Grab at least an AccessShareLock on the parent table */
15001513
parent = heap_open(get_partition_parent(RelationGetRelid(rel)),
15011514
AccessShareLock);
15021515

1503-
/* Quick copy */
1504-
if (rel->rd_partcheck)
1505-
{
1506-
if (parent->rd_rel->relispartition)
1507-
result = list_concat(generate_partition_qual(parent),
1508-
copyObject(rel->rd_partcheck));
1509-
else
1510-
result = copyObject(rel->rd_partcheck);
1511-
1512-
heap_close(parent, AccessShareLock);
1513-
return result;
1514-
}
1515-
15161516
/* Get pg_class.relpartbound */
1517-
if (!rel->rd_rel->relispartition) /* should not happen */
1518-
elog(ERROR, "relation \"%s\" has relispartition = false",
1519-
RelationGetRelationName(rel));
15201517
tuple = SearchSysCache1(RELOID, RelationGetRelid(rel));
15211518
if (!HeapTupleIsValid(tuple))
15221519
elog(ERROR, "cache lookup failed for relation %u",
@@ -1533,20 +1530,22 @@ generate_partition_qual(Relation rel)
15331530

15341531
my_qual = get_qual_from_partbound(rel, parent, bound);
15351532

1536-
/* If requested, add parent's quals to the list (if any) */
1533+
/* Add the parent's quals to the list (if any) */
15371534
if (parent->rd_rel->relispartition)
1538-
{
1539-
List *parent_check;
1540-
1541-
parent_check = generate_partition_qual(parent);
1542-
result = list_concat(parent_check, my_qual);
1543-
}
1535+
result = list_concat(generate_partition_qual(parent), my_qual);
15441536
else
15451537
result = my_qual;
15461538

1547-
/* Save a copy of my_qual in the relcache */
1539+
/*
1540+
* Change Vars to have partition's attnos instead of the parent's.
1541+
* We do this after we concatenate the parent's quals, because
1542+
* we want every Var in it to bear this relation's attnos.
1543+
*/
1544+
result = map_partition_varattnos(result, rel, parent);
1545+
1546+
/* Save a copy in the relcache */
15481547
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
1549-
rel->rd_partcheck = copyObject(my_qual);
1548+
rel->rd_partcheck = copyObject(result);
15501549
MemoryContextSwitchTo(oldcxt);
15511550

15521551
/* Keep the parent locked until commit */

src/backend/commands/tablecmds.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -13429,6 +13429,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1342913429
Oid part_relid = lfirst_oid(lc);
1343013430
Relation part_rel;
1343113431
Expr *constr;
13432+
List *my_constr;
1343213433

1343313434
/* Lock already taken */
1343413435
if (part_relid != RelationGetRelid(attachRel))
@@ -13451,8 +13452,10 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1345113452
tab = ATGetQueueEntry(wqueue, part_rel);
1345213453

1345313454
constr = linitial(partConstraint);
13454-
tab->partition_constraint = make_ands_implicit((Expr *) constr);
13455-
13455+
my_constr = make_ands_implicit((Expr *) constr);
13456+
tab->partition_constraint = map_partition_varattnos(my_constr,
13457+
part_rel,
13458+
rel);
1345613459
/* keep our lock until commit */
1345713460
if (part_rel != attachRel)
1345813461
heap_close(part_rel, NoLock);

src/include/catalog/partition.h

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ extern bool partition_bounds_equal(PartitionKey key,
7777
extern void check_new_partition_bound(char *relname, Relation parent, Node *bound);
7878
extern Oid get_partition_parent(Oid relid);
7979
extern List *get_qual_from_partbound(Relation rel, Relation parent, Node *bound);
80+
extern List *map_partition_varattnos(List *expr, Relation partrel, Relation parent);
8081
extern List *RelationGetPartitionQual(Relation rel);
8182

8283
/* For tuple routing */

src/test/regress/expected/alter_table.out

+30
Original file line numberDiff line numberDiff line change
@@ -3339,3 +3339,33 @@ drop cascades to table part_2
33393339
drop cascades to table part_5
33403340
drop cascades to table part_5_a
33413341
drop cascades to table part_1
3342+
-- more tests for certain multi-level partitioning scenarios
3343+
create table p (a int, b int) partition by range (a, b);
3344+
create table p1 (b int, a int not null) partition by range (b);
3345+
create table p11 (like p1);
3346+
alter table p11 drop a;
3347+
alter table p11 add a int;
3348+
alter table p11 drop a;
3349+
alter table p11 add a int not null;
3350+
-- attnum for key attribute 'a' is different in p, p1, and p11
3351+
select attrelid::regclass, attname, attnum
3352+
from pg_attribute
3353+
where attname = 'a'
3354+
and (attrelid = 'p'::regclass
3355+
or attrelid = 'p1'::regclass
3356+
or attrelid = 'p11'::regclass);
3357+
attrelid | attname | attnum
3358+
----------+---------+--------
3359+
p | a | 1
3360+
p1 | a | 2
3361+
p11 | a | 4
3362+
(3 rows)
3363+
3364+
alter table p1 attach partition p11 for values from (2) to (5);
3365+
insert into p1 (a, b) values (2, 3);
3366+
-- check that partition validation scan correctly detects violating rows
3367+
alter table p attach partition p1 for values from (1, 2) to (1, 10);
3368+
ERROR: partition constraint is violated by some row
3369+
-- cleanup
3370+
drop table p, p1 cascade;
3371+
NOTICE: drop cascades to table p11

src/test/regress/sql/alter_table.sql

+25
Original file line numberDiff line numberDiff line change
@@ -2191,3 +2191,28 @@ ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
21912191

21922192
-- cleanup
21932193
DROP TABLE list_parted, list_parted2, range_parted CASCADE;
2194+
2195+
-- more tests for certain multi-level partitioning scenarios
2196+
create table p (a int, b int) partition by range (a, b);
2197+
create table p1 (b int, a int not null) partition by range (b);
2198+
create table p11 (like p1);
2199+
alter table p11 drop a;
2200+
alter table p11 add a int;
2201+
alter table p11 drop a;
2202+
alter table p11 add a int not null;
2203+
-- attnum for key attribute 'a' is different in p, p1, and p11
2204+
select attrelid::regclass, attname, attnum
2205+
from pg_attribute
2206+
where attname = 'a'
2207+
and (attrelid = 'p'::regclass
2208+
or attrelid = 'p1'::regclass
2209+
or attrelid = 'p11'::regclass);
2210+
2211+
alter table p1 attach partition p11 for values from (2) to (5);
2212+
2213+
insert into p1 (a, b) values (2, 3);
2214+
-- check that partition validation scan correctly detects violating rows
2215+
alter table p attach partition p1 for values from (1, 2) to (1, 10);
2216+
2217+
-- cleanup
2218+
drop table p, p1 cascade;

0 commit comments

Comments
 (0)