Hi Ashutosh, On 2017/03/23 21:48, Ashutosh Bapat wrote: >>> I have fixed all the issues reported till now.
I've tried to fix your 0012 patch (Multi-level partitioned table expansion) considering your message earlier on this thread [1]. Especially the fact that no AppendRelInfo and RelOptInfo are allocated for partitioned child tables as of commit d3cc37f1d [2]. I've fixed expand_inherited_rtentry() such that AppendRelInfo *is* allocated for a partitioned child RTEs whose rte->inh is set to true. Such an RTE is recursively expanded with that RTE the parent. Also as I mentioned elsewhere [3], the multi-level inheritance expansion of partitioned table will break update/delete for partitioned table, which is because inheritance_planner() is not ready to handle inheritance sets structured that way. I tried to refactor inheritance_planner() such that its core logic can be recursively invoked for partitioned child RTEs. The resulting child paths and other auxiliary information related to planning across the hierarchy are maintained in one place using a struct to hold the same in a few flat lists. The refactoring didn't break any existing tests and a couple of new tests are added to check that it indeed works for multi-level partitioned tables expanded using new multi-level structure. There is some test failure in 0014 (Multi-level partition-wise join tests), probably because of the changes I made to 0012, which I didn't get time to check why, although I've checked using an example that multi-level join planning still works, so it's not completely broken either. Thanks, Amit [1] https://www.postgresql.org/message-id/CAFjFpRefs5ZMnxQ2vP9v5zOtWtNPuiMYc01sb1SWjCOB1CT%3DuQ%40mail.gmail.com [2] https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=d3cc37f1d [3] https://www.postgresql.org/message-id/744d20fe-fc7b-f89e-8d06-6496ec537b86%40lab.ntt.co.jp
>From 7051b9cb4760908e23e64969988b58fcb466868e Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat <ashutosh.ba...@enterprisedb.com> Date: Fri, 10 Feb 2017 13:50:14 +0530 Subject: [PATCH 12/14] Multi-level partitioned table expansion. Construct inheritance hierarchy of a partitioned table to reflect the partition hierarchy. Commit d3cc37f1d got rid of AppendRelInfos for *all* partitioned child child tables. But we'd need one for recursively expandded partitioned children. In its absence, some of the later planning stages would not be able to access the partitions below the first level, because they are not directly linked to the root parent. Fix that by allocating one. That means such children also get a RelOptInfo, which also wasn't allocated previously. Expanding partitioned table inheritance this new way means we need to teach a few places how to traverse the hierarchy, such as propagating lateral join information from the root table down the partition hierarchy. Further, inheritance_planner() needs refactoring to enable recursive invocation of its core logic. While the query_planner() can handle the new partition hierarchical structure just fine because of the way the relevant code works, inheritance_planner() would miss any child tables below the first level, because it can't do recursion. Refactor it so that it can. --- src/backend/optimizer/plan/initsplan.c | 14 +- src/backend/optimizer/plan/planner.c | 290 +++++++++++++++++++++------------ src/backend/optimizer/prep/prepunion.c | 69 ++++++-- src/test/regress/expected/inherit.out | 22 +++ src/test/regress/sql/inherit.sql | 17 ++ 5 files changed, 292 insertions(+), 120 deletions(-) diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index b4ac224a7a..20774b29fe 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -630,7 +630,19 @@ create_lateral_join_info(PlannerInfo *root) { RelOptInfo *brel = root->simple_rel_array[rti]; - if (brel == NULL || brel->reloptkind != RELOPT_BASEREL) + if (brel == NULL) + continue; + + /* + * If an "other rel" RTE is a "partitioned table", we must propagate + * the lateral info inherited all the way from the root parent to its + * children. That's because the children are not linked directly with + * the root parent via AppendRelInfo's unlike in case of a regular + * inheritance set (see expand_inherited_rtentry()). Failing to + * do this would result in those children not getting marked with the + * appropriate lateral info. + */ + if (brel->reloptkind != RELOPT_BASEREL && !brel->part_scheme) continue; if (root->simple_rte_array[rti]->inh) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 4487683196..59cb559b9e 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -91,10 +91,28 @@ typedef struct List *groupClause; /* overrides parse->groupClause */ } standard_qp_extra; +/* Result of a given invocation of inheritance_planner_guts() */ +typedef struct +{ + Index nominalRelation; + List *partitioned_rels; + List *resultRelations; + List *subpaths; + List *subroots; + List *withCheckOptionLists; + List *returningLists; + List *final_rtable; + List *init_plans; + int save_rel_array_size; + RelOptInfo **save_rel_array; +} inheritance_planner_result; + /* Local functions */ static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind); static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode); static void inheritance_planner(PlannerInfo *root); +static void inheritance_planner_guts(PlannerInfo *root, + inheritance_planner_result *inhpres); static void grouping_planner(PlannerInfo *root, bool inheritance_update, double tuple_fraction); static void preprocess_rowmarks(PlannerInfo *root); @@ -994,25 +1012,105 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr) static void inheritance_planner(PlannerInfo *root) { + inheritance_planner_result inhpres; + Query *parse = root->parse; + RelOptInfo *final_rel; + Index rti; + int final_rtable_len; + ListCell *lc; + List *rowMarks; + + /* + * Away we go... Although the inheritance hierarchy to be processed might + * be represented in a non-flat manner, some of the elements needed to + * create the final ModifyTable path are always returned in a flat list + * structure. + */ + memset(&inhpres, 0, sizeof(inhpres)); + inheritance_planner_guts(root, &inhpres); + + /* Result path must go into outer query's FINAL upperrel */ + final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL); + + /* + * We don't currently worry about setting final_rel's consider_parallel + * flag in this case, nor about allowing FDWs or create_upper_paths_hook + * to get control here. + */ + + /* + * If we managed to exclude every child rel, return a dummy plan; it + * doesn't even need a ModifyTable node. + */ + if (inhpres.subpaths == NIL) + { + set_dummy_rel_pathlist(final_rel); + return; + } + + /* + * Put back the final adjusted rtable into the master copy of the Query. + * (We mustn't do this if we found no non-excluded children.) + */ + parse->rtable = inhpres.final_rtable; + root->simple_rel_array_size = inhpres.save_rel_array_size; + root->simple_rel_array = inhpres.save_rel_array; + /* Must reconstruct master's simple_rte_array, too */ + final_rtable_len = list_length(inhpres.final_rtable); + root->simple_rte_array = (RangeTblEntry **) + palloc0((final_rtable_len + 1) * + sizeof(RangeTblEntry *)); + rti = 1; + foreach(lc, inhpres.final_rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + + root->simple_rte_array[rti++] = rte; + } + + /* + * If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will + * have dealt with fetching non-locked marked rows, else we need to have + * ModifyTable do that. + */ + if (parse->rowMarks) + rowMarks = NIL; + else + rowMarks = root->rowMarks; + + /* Create Path representing a ModifyTable to do the UPDATE/DELETE work */ + add_path(final_rel, (Path *) + create_modifytable_path(root, final_rel, + parse->commandType, + parse->canSetTag, + inhpres.nominalRelation, + inhpres.partitioned_rels, + inhpres.resultRelations, + inhpres.subpaths, + inhpres.subroots, + inhpres.withCheckOptionLists, + inhpres.returningLists, + rowMarks, + NULL, + SS_assign_special_param(root))); +} + +/* + * inheritance_planner_guts + * Recursive guts of inheritance_planner + */ +static void +inheritance_planner_guts(PlannerInfo *root, + inheritance_planner_result *inhpres) +{ Query *parse = root->parse; int parentRTindex = parse->resultRelation; Bitmapset *subqueryRTindexes; Bitmapset *modifiableARIindexes; - int nominalRelation = -1; - List *final_rtable = NIL; - int save_rel_array_size = 0; - RelOptInfo **save_rel_array = NULL; - List *subpaths = NIL; - List *subroots = NIL; - List *resultRelations = NIL; - List *withCheckOptionLists = NIL; - List *returningLists = NIL; - List *rowMarks; - RelOptInfo *final_rel; + bool nominalRelationSet = false; ListCell *lc; Index rti; RangeTblEntry *parent_rte; - List *partitioned_rels = NIL; Assert(parse->commandType != CMD_INSERT); @@ -1080,7 +1178,10 @@ inheritance_planner(PlannerInfo *root) */ parent_rte = rt_fetch(parentRTindex, root->parse->rtable); if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE) - nominalRelation = parentRTindex; + { + inhpres->nominalRelation = parentRTindex; + nominalRelationSet = true; + } /* * And now we can get on with generating a plan for each child table. @@ -1089,6 +1190,7 @@ inheritance_planner(PlannerInfo *root) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc); PlannerInfo *subroot; + Index childRTindex = appinfo->child_relid; RangeTblEntry *child_rte; RelOptInfo *sub_final_rel; Path *subpath; @@ -1120,7 +1222,7 @@ inheritance_planner(PlannerInfo *root) * child rel (they've already been transformed properly for that). */ parent_rte = rt_fetch(parentRTindex, subroot->parse->rtable); - child_rte = rt_fetch(appinfo->child_relid, subroot->parse->rtable); + child_rte = rt_fetch(childRTindex, subroot->parse->rtable); child_rte->securityQuals = parent_rte->securityQuals; parent_rte->securityQuals = NIL; @@ -1165,7 +1267,8 @@ inheritance_planner(PlannerInfo *root) * These won't be referenced, so there's no need to make them very * valid-looking. */ - while (list_length(subroot->parse->rtable) < list_length(final_rtable)) + while (list_length(subroot->parse->rtable) < + list_length(inhpres->final_rtable)) subroot->parse->rtable = lappend(subroot->parse->rtable, makeNode(RangeTblEntry)); @@ -1177,7 +1280,7 @@ inheritance_planner(PlannerInfo *root) * since subquery RTEs couldn't contain any references to the target * rel. */ - if (final_rtable != NIL && subqueryRTindexes != NULL) + if (inhpres->final_rtable != NIL && subqueryRTindexes != NULL) { ListCell *lr; @@ -1222,6 +1325,47 @@ inheritance_planner(PlannerInfo *root) } } + /* + * Recurse for a partitioned child table. We shouldn't be planning + * a partitioned RTE as a child member, which is what the code after + * this block does. + */ + if (child_rte->inh) + { + inheritance_planner_result child_inhpres; + + Assert(child_rte->relkind == RELKIND_PARTITIONED_TABLE); + + /* During the recursive invocation, this child is the parent. */ + subroot->parse->resultRelation = childRTindex; + memset(&child_inhpres, 0, sizeof(child_inhpres)); + inheritance_planner_guts(subroot, &child_inhpres); + + inhpres->partitioned_rels = list_concat(inhpres->partitioned_rels, + child_inhpres.partitioned_rels); + inhpres->resultRelations = list_concat(inhpres->resultRelations, + child_inhpres.resultRelations); + inhpres->subpaths = list_concat(inhpres->subpaths, + child_inhpres.subpaths); + inhpres->subroots = list_concat(inhpres->subroots, + child_inhpres.subroots); + inhpres->withCheckOptionLists = + list_concat(inhpres->withCheckOptionLists, + child_inhpres.withCheckOptionLists); + inhpres->returningLists = list_concat(inhpres->returningLists, + child_inhpres.returningLists); + if (child_inhpres.final_rtable != NIL) + inhpres->final_rtable = child_inhpres.final_rtable; + if (child_inhpres.init_plans != NIL) + inhpres->init_plans = child_inhpres.init_plans; + if (child_inhpres.save_rel_array_size != 0) + { + inhpres->save_rel_array_size = child_inhpres.save_rel_array_size; + inhpres->save_rel_array = child_inhpres.save_rel_array; + } + continue; + } + /* There shouldn't be any OJ info to translate, as yet */ Assert(subroot->join_info_list == NIL); /* and we haven't created PlaceHolderInfos, either */ @@ -1253,8 +1397,11 @@ inheritance_planner(PlannerInfo *root) * the duplicate child RTE added for the parent does not appear * anywhere else in the plan tree. */ - if (nominalRelation < 0) - nominalRelation = appinfo->child_relid; + if (!nominalRelationSet) + { + inhpres->nominalRelation = childRTindex; + nominalRelationSet = true; + } /* * Select cheapest path in case there's more than one. We always run @@ -1277,12 +1424,12 @@ inheritance_planner(PlannerInfo *root) * becomes the initial contents of final_rtable; otherwise, append * just its modified subquery RTEs to final_rtable. */ - if (final_rtable == NIL) - final_rtable = subroot->parse->rtable; + if (inhpres->final_rtable == NIL) + inhpres->final_rtable = subroot->parse->rtable; else - final_rtable = list_concat(final_rtable, - list_copy_tail(subroot->parse->rtable, - list_length(final_rtable))); + inhpres->final_rtable = list_concat(inhpres->final_rtable, + list_copy_tail(subroot->parse->rtable, + list_length(inhpres->final_rtable))); /* * We need to collect all the RelOptInfos from all child plans into @@ -1291,109 +1438,48 @@ inheritance_planner(PlannerInfo *root) * have to propagate forward the RelOptInfos that were already built * in previous children. */ - Assert(subroot->simple_rel_array_size >= save_rel_array_size); - for (rti = 1; rti < save_rel_array_size; rti++) + Assert(subroot->simple_rel_array_size >= inhpres->save_rel_array_size); + for (rti = 1; rti < inhpres->save_rel_array_size; rti++) { - RelOptInfo *brel = save_rel_array[rti]; + RelOptInfo *brel = inhpres->save_rel_array[rti]; if (brel) subroot->simple_rel_array[rti] = brel; } - save_rel_array_size = subroot->simple_rel_array_size; - save_rel_array = subroot->simple_rel_array; + inhpres->save_rel_array_size = subroot->simple_rel_array_size; + inhpres->save_rel_array = subroot->simple_rel_array; /* Make sure any initplans from this rel get into the outer list */ - root->init_plans = subroot->init_plans; + inhpres->init_plans = subroot->init_plans; /* Build list of sub-paths */ - subpaths = lappend(subpaths, subpath); + inhpres->subpaths = lappend(inhpres->subpaths, subpath); /* Build list of modified subroots, too */ - subroots = lappend(subroots, subroot); + inhpres->subroots = lappend(inhpres->subroots, subroot); /* Build list of target-relation RT indexes */ - resultRelations = lappend_int(resultRelations, appinfo->child_relid); + inhpres->resultRelations = lappend_int(inhpres->resultRelations, + childRTindex); /* Build lists of per-relation WCO and RETURNING targetlists */ if (parse->withCheckOptions) - withCheckOptionLists = lappend(withCheckOptionLists, - subroot->parse->withCheckOptions); + inhpres->withCheckOptionLists = + lappend(inhpres->withCheckOptionLists, + subroot->parse->withCheckOptions); if (parse->returningList) - returningLists = lappend(returningLists, - subroot->parse->returningList); - + inhpres->returningLists = lappend(inhpres->returningLists, + subroot->parse->returningList); Assert(!parse->onConflict); } if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE) { - partitioned_rels = get_partitioned_child_rels(root, parentRTindex); + inhpres->partitioned_rels = get_partitioned_child_rels(root, + parentRTindex); /* The root partitioned table is included as a child rel */ - Assert(list_length(partitioned_rels) >= 1); - } - - /* Result path must go into outer query's FINAL upperrel */ - final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL); - - /* - * We don't currently worry about setting final_rel's consider_parallel - * flag in this case, nor about allowing FDWs or create_upper_paths_hook - * to get control here. - */ - - /* - * If we managed to exclude every child rel, return a dummy plan; it - * doesn't even need a ModifyTable node. - */ - if (subpaths == NIL) - { - set_dummy_rel_pathlist(final_rel); - return; + Assert(list_length(inhpres->partitioned_rels) >= 1); } - - /* - * Put back the final adjusted rtable into the master copy of the Query. - * (We mustn't do this if we found no non-excluded children.) - */ - parse->rtable = final_rtable; - root->simple_rel_array_size = save_rel_array_size; - root->simple_rel_array = save_rel_array; - /* Must reconstruct master's simple_rte_array, too */ - root->simple_rte_array = (RangeTblEntry **) - palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *)); - rti = 1; - foreach(lc, final_rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - root->simple_rte_array[rti++] = rte; - } - - /* - * If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will - * have dealt with fetching non-locked marked rows, else we need to have - * ModifyTable do that. - */ - if (parse->rowMarks) - rowMarks = NIL; - else - rowMarks = root->rowMarks; - - /* Create Path representing a ModifyTable to do the UPDATE/DELETE work */ - add_path(final_rel, (Path *) - create_modifytable_path(root, final_rel, - parse->commandType, - parse->canSetTag, - nominalRelation, - partitioned_rels, - resultRelations, - subpaths, - subroots, - withCheckOptionLists, - returningLists, - rowMarks, - NULL, - SS_assign_special_param(root))); } /*-------------------- diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 2bbef3acbd..cb7d9b0c43 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -97,7 +97,7 @@ static List *generate_append_tlist(List *colTypes, List *colCollations, List *input_tlists, List *refnames_tlist); static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist); -static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, +static List *expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti); static void make_inh_translation_list(Relation oldrelation, Relation newrelation, @@ -1319,19 +1319,22 @@ expand_inherited_tables(PlannerInfo *root) Index nrtes; Index rti; ListCell *rl; + Query *parse = root->parse; /* * expand_inherited_rtentry may add RTEs to parse->rtable; there is no * need to scan them since they can't have inh=true. So just scan as far * as the original end of the rtable list. */ - nrtes = list_length(root->parse->rtable); - rl = list_head(root->parse->rtable); + nrtes = list_length(parse->rtable); + rl = list_head(parse->rtable); for (rti = 1; rti <= nrtes; rti++) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl); + List *appinfos; - expand_inherited_rtentry(root, rte, rti); + appinfos = expand_inherited_rtentry(root, rte, rti); + root->append_rel_list = list_concat(root->append_rel_list, appinfos); rl = lnext(rl); } } @@ -1351,8 +1354,10 @@ expand_inherited_tables(PlannerInfo *root) * * A childless table is never considered to be an inheritance set; therefore * a parent RTE must always have at least two associated AppendRelInfos. + * + * Returns a list of AppendRelInfos, or NIL. */ -static void +static List* expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) { Query *parse = root->parse; @@ -1369,12 +1374,12 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) /* Does RT entry allow inheritance? */ if (!rte->inh) - return; + return NIL; /* Ignore any already-expanded UNION ALL nodes */ if (rte->rtekind != RTE_RELATION) { Assert(rte->rtekind == RTE_SUBQUERY); - return; + return NIL; } /* Fast path for common case of childless table */ parentOID = rte->relid; @@ -1382,7 +1387,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) { /* Clear flag before returning */ rte->inh = false; - return; + return NIL; } /* @@ -1406,8 +1411,19 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) else lockmode = AccessShareLock; - /* Scan for all members of inheritance set, acquire needed locks */ - inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); + /* + * Expand partitioned table level-wise to help optimizations like + * partition-wise join which match partitions at every level. Otherwise, + * scan for all members of inheritance set. Acquire needed locks + */ + if (rte->relkind == RELKIND_PARTITIONED_TABLE) + { + inhOIDs = list_make1_oid(parentOID); + inhOIDs = list_concat(inhOIDs, + find_inheritance_children(parentOID, lockmode)); + } + else + inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); /* * Check that there's at least one descendant, else treat as no-child @@ -1418,7 +1434,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) { /* Clear flag before returning */ rte->inh = false; - return; + return NIL; } /* @@ -1446,6 +1462,12 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) Index childRTindex; AppendRelInfo *appinfo; + /* + * If this child is a partitioned table, this contains AppendRelInfos + * for its own children. + */ + List *myappinfos; + /* Open rel if needed; we already have required locks */ if (childOID != parentOID) newrelation = heap_open(childOID, NoLock); @@ -1479,7 +1501,12 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) childrte = copyObject(rte); childrte->relid = childOID; childrte->relkind = newrelation->rd_rel->relkind; - childrte->inh = false; + /* A partitioned child will need to be expanded further. */ + if (childOID != parentOID && + childrte->relkind == RELKIND_PARTITIONED_TABLE) + childrte->inh = true; + else + childrte->inh = false; childrte->requiredPerms = 0; childrte->securityQuals = NIL; parse->rtable = lappend(parse->rtable, childrte); @@ -1487,9 +1514,9 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) /* * Build an AppendRelInfo for this parent and child, unless the child - * is a partitioned table. + * RTE simply duplicates the parent *partitioned* table. */ - if (childrte->relkind != RELKIND_PARTITIONED_TABLE) + if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh) { need_append = true; appinfo = makeNode(AppendRelInfo); @@ -1559,6 +1586,14 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) /* Close child relations, but keep locks */ if (childOID != parentOID) heap_close(newrelation, NoLock); + + /* Expand partitioned children recursively. */ + if (childrte->inh) + { + myappinfos = expand_inherited_rtentry(root, childrte, + childRTindex); + appinfos = list_concat(appinfos, myappinfos); + } } heap_close(oldrelation, NoLock); @@ -1574,7 +1609,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) { /* Clear flag before returning */ rte->inh = false; - return; + return NIL; } /* @@ -1595,8 +1630,8 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) root->pcinfo_list = lappend(root->pcinfo_list, pcinfo); } - /* Otherwise, OK to add to root->append_rel_list */ - root->append_rel_list = list_concat(root->append_rel_list, appinfos); + /* The following will be concatenated to root->append_rel_list. */ + return appinfos; } /* diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 6163ed8117..7a969f2f10 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -625,6 +625,28 @@ select tableoid::regclass::text as relname, parted_tab.* from parted_tab order b (3 rows) drop table parted_tab; +-- Check UPDATE with *multi-level partitioned* inherited target +create table mlparted_tab (a int, b char, c text) partition by list (a); +create table mlparted_tab_part1 partition of mlparted_tab for values in (1); +create table mlparted_tab_part2 partition of mlparted_tab for values in (2) partition by list (b); +create table mlparted_tab_part3 partition of mlparted_tab for values in (3); +create table mlparted_tab_part2a partition of mlparted_tab_part2 for values in ('a'); +create table mlparted_tab_part2b partition of mlparted_tab_part2 for values in ('b'); +insert into mlparted_tab values (1, 'a'), (2, 'a'), (2, 'b'), (3, 'a'); +update mlparted_tab mlp set c = 'xxx' +from + (select a from some_tab union all select a+1 from some_tab) ss (a) +where (mlp.a = ss.a and mlp.b = 'b') or mlp.a = 3; +select tableoid::regclass::text as relname, mlparted_tab.* from mlparted_tab order by 1,2; + relname | a | b | c +---------------------+---+---+----- + mlparted_tab_part1 | 1 | a | + mlparted_tab_part2a | 2 | a | + mlparted_tab_part2b | 2 | b | xxx + mlparted_tab_part3 | 3 | a | xxx +(4 rows) + +drop table mlparted_tab; drop table some_tab cascade; NOTICE: drop cascades to table some_tab_child /* Test multiple inheritance of column defaults */ diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql index d43b75c4a7..b814a4c471 100644 --- a/src/test/regress/sql/inherit.sql +++ b/src/test/regress/sql/inherit.sql @@ -154,6 +154,23 @@ where parted_tab.a = ss.a; select tableoid::regclass::text as relname, parted_tab.* from parted_tab order by 1,2; drop table parted_tab; + +-- Check UPDATE with *multi-level partitioned* inherited target +create table mlparted_tab (a int, b char, c text) partition by list (a); +create table mlparted_tab_part1 partition of mlparted_tab for values in (1); +create table mlparted_tab_part2 partition of mlparted_tab for values in (2) partition by list (b); +create table mlparted_tab_part3 partition of mlparted_tab for values in (3); +create table mlparted_tab_part2a partition of mlparted_tab_part2 for values in ('a'); +create table mlparted_tab_part2b partition of mlparted_tab_part2 for values in ('b'); +insert into mlparted_tab values (1, 'a'), (2, 'a'), (2, 'b'), (3, 'a'); + +update mlparted_tab mlp set c = 'xxx' +from + (select a from some_tab union all select a+1 from some_tab) ss (a) +where (mlp.a = ss.a and mlp.b = 'b') or mlp.a = 3; +select tableoid::regclass::text as relname, mlparted_tab.* from mlparted_tab order by 1,2; + +drop table mlparted_tab; drop table some_tab cascade; /* Test multiple inheritance of column defaults */ -- 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