On 2016/12/16 17:02, Amit Langote wrote: > [PATCH 2/7] Change how RelationGetPartitionQual() and related code works > > Since we always want to recurse, ie, include the parent's partition > constraint (if any), get rid of the argument recurse. > > Refactor out the code doing the mapping of attnos of Vars in partition > constraint expressions (parent attnos -> child attnos). Move it to a > separate function map_partition_varattnos() and call it from appropriate > places. It previously used to be done in get_qual_from_partbound(), > which would lead to wrong results in certain multi-level partitioning > cases, as the mapping would be done for immediate parent-partition pairs. > Now in generate_partition_qual() which is the workhorse of > RelationGetPartitionQual(), we first generate the full expression > (considering all levels of partitioning) and then do the mapping from the > root parent to a leaf partition. It is also possible to generate > partition constraint up to certain non-leaf level and then apply the > same to leaf partitions of that sub-tree after suitable substitution > of varattnos using the new map_partition_varattnos() directly. > > Bug fix: ATExecAttachPartition() failed to do the mapping when attaching > a partitioned table as partition. It is possible for the partitions of > such table to have different attributes from the table being attached > and/or the target partitioned table.
Oops, PATCH 2/7 attached with the previous email had a bug in it, whereby map_partition_varattnos() was not applied to the partition constraint expressions returned directly from the relcache (rd_partcheck) copy. Attaching just the updated (only) PATCH 2 which fixes that. Patches 1,3,4,5,6,7/7 from the previous email [1] are fine. Sorry about that. Thanks, Amit [1] https://www.postgresql.org/message-id/c820c0eb-6935-6f84-8c6a-785fdff13...@lab.ntt.co.jp
>From b10228cfbdefc578138553b29a7658fb78c32de3 Mon Sep 17 00:00:00 2001 From: amit <amitlangot...@gmail.com> Date: Thu, 15 Dec 2016 16:27:04 +0900 Subject: [PATCH 2/7] Change how RelationGetPartitionQual() and related code works Since we always want to recurse, ie, include the parent's partition constraint (if any), get rid of the argument recurse. Refactor out the code doing the mapping of attnos of Vars in partition constraint expressions (parent attnos -> child attnos). Move it to a separate function map_partition_varattnos() and call it from appropriate places. It previously used to be done in get_qual_from_partbound(), which would lead to wrong results in certain multi-level partitioning cases, as the mapping would be done for immediate parent-partition pairs. Now in generate_partition_qual() which is the workhorse of RelationGetPartitionQual(), we first generate the full expression (considering all levels of partitioning) and then do the mapping from the root parent to a leaf partition. It is also possible to generate partition constraint up to certain non-leaf level and then apply the same to leaf partitions of that sub-tree after suitable substitution of varattnos using the new map_partition_varattnos() directly. Bug fix: ATExecAttachPartition() failed to do the mapping when attaching a partitioned table as partition. It is possible for the partitions of such table to have different attributes from the table being attached and/or the target partitioned table. --- src/backend/catalog/partition.c | 97 ++++++++++++++++++++---------------- src/backend/commands/tablecmds.c | 9 ++-- src/backend/executor/execMain.c | 3 +- src/backend/optimizer/util/plancat.c | 2 +- src/include/catalog/partition.h | 3 +- 5 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 9980582b77..694cb469e0 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -122,7 +122,7 @@ static List *get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec); static List *get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec); static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel); -static List *generate_partition_qual(Relation rel, bool recurse); +static List *generate_partition_qual(Relation rel); static PartitionRangeBound *make_one_range_bound(PartitionKey key, int index, List *datums, bool lower); @@ -850,10 +850,6 @@ get_qual_from_partbound(Relation rel, Relation parent, Node *bound) PartitionBoundSpec *spec = (PartitionBoundSpec *) bound; PartitionKey key = RelationGetPartitionKey(parent); List *my_qual = NIL; - TupleDesc parent_tupdesc = RelationGetDescr(parent); - AttrNumber parent_attno; - AttrNumber *partition_attnos; - bool found_whole_row; Assert(key != NULL); @@ -874,38 +870,48 @@ get_qual_from_partbound(Relation rel, Relation parent, Node *bound) (int) key->strategy); } - /* - * Translate vars in the generated expression to have correct attnos. Note - * that the vars in my_qual bear attnos dictated by key which carries - * physical attnos of the parent. We must allow for a case where physical - * attnos of a partition can be different from the parent. - */ - partition_attnos = (AttrNumber *) - palloc0(parent_tupdesc->natts * sizeof(AttrNumber)); - for (parent_attno = 1; parent_attno <= parent_tupdesc->natts; - parent_attno++) + return my_qual; +} + +/* + * map_partition_varattnos - maps varattno of any Vars in expr from the + * parent attno to partition attno. + * + * We must allow for a case where physical attnos of a partition can be + * different from the parent's. + */ +List * +map_partition_varattnos(List *expr, Relation partrel, Relation parent) +{ + TupleDesc tupdesc = RelationGetDescr(parent); + AttrNumber attno; + AttrNumber *part_attnos; + bool found_whole_row; + + part_attnos = (AttrNumber *) palloc0(tupdesc->natts * sizeof(AttrNumber)); + for (attno = 1; attno <= tupdesc->natts; attno++) { - Form_pg_attribute attribute = parent_tupdesc->attrs[parent_attno - 1]; + Form_pg_attribute attribute = tupdesc->attrs[attno - 1]; char *attname = NameStr(attribute->attname); - AttrNumber partition_attno; + AttrNumber part_attno; if (attribute->attisdropped) continue; - partition_attno = get_attnum(RelationGetRelid(rel), attname); - partition_attnos[parent_attno - 1] = partition_attno; + part_attno = get_attnum(RelationGetRelid(partrel), attname); + part_attnos[attno - 1] = part_attno; } - my_qual = (List *) map_variable_attnos((Node *) my_qual, - 1, 0, - partition_attnos, - parent_tupdesc->natts, - &found_whole_row); - /* there can never be a whole-row reference here */ + expr = (List *) map_variable_attnos((Node *) expr, + 1, 0, + part_attnos, + tupdesc->natts, + &found_whole_row); + /* There can never be a whole-row reference here */ if (found_whole_row) elog(ERROR, "unexpected whole-row reference found in partition key"); - return my_qual; + return expr; } /* @@ -914,13 +920,13 @@ get_qual_from_partbound(Relation rel, Relation parent, Node *bound) * Returns a list of partition quals */ List * -RelationGetPartitionQual(Relation rel, bool recurse) +RelationGetPartitionQual(Relation rel) { /* Quick exit */ if (!rel->rd_rel->relispartition) return NIL; - return generate_partition_qual(rel, recurse); + return generate_partition_qual(rel); } /* Turn an array of OIDs with N elements into a list */ @@ -1445,7 +1451,7 @@ get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, * into cache memory. */ static List * -generate_partition_qual(Relation rel, bool recurse) +generate_partition_qual(Relation rel) { HeapTuple tuple; MemoryContext oldcxt; @@ -1459,6 +1465,10 @@ generate_partition_qual(Relation rel, bool recurse) /* Guard against stack overflow due to overly deep partition tree */ check_stack_depth(); + /* Recursive callers may not have checked themselves */ + if (!rel->rd_rel->relispartition) + return NIL; + /* Grab at least an AccessShareLock on the parent table */ parent = heap_open(get_partition_parent(RelationGetRelid(rel)), AccessShareLock); @@ -1466,13 +1476,14 @@ generate_partition_qual(Relation rel, bool recurse) /* Quick copy */ if (rel->rd_partcheck) { - if (parent->rd_rel->relispartition && recurse) - result = list_concat(generate_partition_qual(parent, true), - copyObject(rel->rd_partcheck)); - else - result = copyObject(rel->rd_partcheck); + result = list_concat(generate_partition_qual(parent), + copyObject(rel->rd_partcheck)); - heap_close(parent, AccessShareLock); + /* Mark Vars with correct attnos */ + result = map_partition_varattnos(result, rel, parent); + + /* Keep the parent locked until commit */ + heap_close(parent, NoLock); return result; } @@ -1492,18 +1503,16 @@ generate_partition_qual(Relation rel, bool recurse) my_qual = get_qual_from_partbound(rel, parent, bound); - /* If requested, add parent's quals to the list (if any) */ - if (parent->rd_rel->relispartition && recurse) - { - List *parent_check; - - parent_check = generate_partition_qual(parent, true); - result = list_concat(parent_check, my_qual); - } + /* Add the parent's quals to the list (if any) */ + if (parent->rd_rel->relispartition) + result = list_concat(generate_partition_qual(parent), my_qual); else result = my_qual; - /* Save a copy of my_qual in the relcache */ + /* Mark Vars with correct attnos */ + result = map_partition_varattnos(result, rel, parent); + + /* Save a copy of *only* the partition's qual in the relcache */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); rel->rd_partcheck = copyObject(my_qual); MemoryContextSwitchTo(oldcxt); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1c219b03dd..d2e4cfc365 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -13151,7 +13151,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) */ partConstraint = list_concat(get_qual_from_partbound(attachRel, rel, cmd->bound), - RelationGetPartitionQual(rel, true)); + RelationGetPartitionQual(rel)); partConstraint = (List *) eval_const_expressions(NULL, (Node *) partConstraint); partConstraint = (List *) canonicalize_qual((Expr *) partConstraint); @@ -13323,6 +13323,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) Oid part_relid = lfirst_oid(lc); Relation part_rel; Expr *constr; + List *my_constr; /* Lock already taken */ if (part_relid != RelationGetRelid(attachRel)) @@ -13345,8 +13346,10 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) tab = ATGetQueueEntry(wqueue, part_rel); constr = linitial(partConstraint); - tab->partition_constraint = make_ands_implicit((Expr *) constr); - + my_constr = make_ands_implicit((Expr *) constr); + tab->partition_constraint = map_partition_varattnos(my_constr, + part_rel, + rel); /* keep our lock until commit */ if (part_rel != attachRel) heap_close(part_rel, NoLock); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d43a204808..520fe4e0ce 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1256,8 +1256,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_projectReturning = NULL; if (load_partition_check) resultRelInfo->ri_PartitionCheck = - RelationGetPartitionQual(resultRelationDesc, - true); + RelationGetPartitionQual(resultRelationDesc); } /* diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 72272d9bb7..150229ed6d 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -1228,7 +1228,7 @@ get_relation_constraints(PlannerInfo *root, } /* Append partition predicates, if any */ - pcqual = RelationGetPartitionQual(relation, true); + pcqual = RelationGetPartitionQual(relation); if (pcqual) { /* diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index 21effbf87b..6ff821e6cf 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -70,7 +70,8 @@ extern bool partition_bounds_equal(PartitionKey key, extern void check_new_partition_bound(char *relname, Relation parent, Node *bound); extern Oid get_partition_parent(Oid relid); extern List *get_qual_from_partbound(Relation rel, Relation parent, Node *bound); -extern List *RelationGetPartitionQual(Relation rel, bool recurse); +extern List *map_partition_varattnos(List *expr, Relation partrel, Relation parent); +extern List *RelationGetPartitionQual(Relation rel); /* For tuple routing */ extern PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel, -- 2.11.0
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers