From 7d8c20357ecd031bda163b830d66141f2c3e7346 Mon Sep 17 00:00:00 2001 From: amit Date: Mon, 4 Mar 2019 15:52:11 +0900 Subject: [PATCH v26 3/6] Adjust inheritance_planner to reuse source child tables Set of source inheritance child tables won't change across repeated planning of the query for different target child relations. So, note down the RangeTblEntrys, AppendRelInfos, and PlanRowMarks of the source inheritance child tables after the planning for the first child table is finished. When planning for the subsequent child target tables, put the saved objects of source inheritance child tables into the child PlannerInfos and set contains_inherit_children to true, so that query_planner doesn't add them again. This restores the regression test outputs to their original state, because like before, source inheritance child tables are not added multiple times. --- src/backend/optimizer/plan/planner.c | 216 +++++++++++++++++--------- src/test/regress/expected/partition_prune.out | 12 +- src/test/regress/expected/rowsecurity.out | 16 +- 3 files changed, 157 insertions(+), 87 deletions(-) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index c71a40e6ba..94557108e8 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1206,10 +1206,15 @@ inheritance_planner(PlannerInfo *root) Index rti; RangeTblEntry *parent_rte; PlannerInfo *parent_root; - Query *parent_parse; Bitmapset *parent_relids = bms_make_singleton(top_parentRTindex); PlannerInfo **parent_roots = NULL; List *orig_append_rel_list = list_copy(root->append_rel_list); + List *orig_row_marks = list_copy(root->rowMarks); + List *orig_rtable = list_copy(root->parse->rtable); + List *rtable_with_target; + List *source_appinfos = NIL; + List *source_child_rowmarks = NIL; + List *source_child_rtes = NIL; Assert(parse->commandType != CMD_INSERT); @@ -1233,6 +1238,13 @@ inheritance_planner(PlannerInfo *root) return; } + + /* + * This one also contains the child target relations, but no other + * child relations. + */ + rtable_with_target = list_copy(root->parse->rtable); + /* * We generate a modified instance of the original Query for each target * relation, plan that, and put all the plans into a list that will be @@ -1249,10 +1261,13 @@ inheritance_planner(PlannerInfo *root) * management of the rowMarks list. * * To begin with, generate a bitmapset of the relids of the subquery RTEs. + * We use orig_rtable, not rtable_with_target (parse->rtable), because we + * wouldn't need to consider any newly added RTEs as they all must be + * RTE_RELATION entries. */ subqueryRTindexes = NULL; rti = 1; - foreach(lc, parse->rtable) + foreach(lc, orig_rtable) { RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc); @@ -1265,26 +1280,24 @@ inheritance_planner(PlannerInfo *root) * Next, we want to identify which AppendRelInfo items contain references * to any of the aforesaid subquery RTEs. These items will need to be * copied and modified to adjust their subquery references; whereas the - * other ones need not be touched. It's worth being tense over this - * because we can usually avoid processing most of the AppendRelInfo - * items, thereby saving O(N^2) space and time when the target is a large - * inheritance tree. We can identify AppendRelInfo items by their - * child_relid, since that should be unique within the list. + * other ones need not be touched. We can assume that all (if any) + * entries in orig_append_rel_list contain references to subquery RTEs, + * because they correspond to flattneed UNION ALL subqueries. Especially, + * we don't need to bother with those added when adding the child target + * relations. We can identify AppendRelInfo items by their child_relid, + * since that should be unique within the list. */ modifiableARIindexes = NULL; - if (subqueryRTindexes != NULL) + foreach(lc, orig_append_rel_list) { - foreach(lc, root->append_rel_list) - { - AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc); + AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc); - if (bms_is_member(appinfo->parent_relid, subqueryRTindexes) || - bms_is_member(appinfo->child_relid, subqueryRTindexes) || - bms_overlap(pull_varnos((Node *) appinfo->translated_vars), - subqueryRTindexes)) - modifiableARIindexes = bms_add_member(modifiableARIindexes, - appinfo->child_relid); - } + Assert(bms_is_member(appinfo->parent_relid, subqueryRTindexes) || + bms_is_member(appinfo->child_relid, subqueryRTindexes) || + bms_overlap(pull_varnos((Node *) appinfo->translated_vars), + subqueryRTindexes)); + modifiableARIindexes = bms_add_member(modifiableARIindexes, + appinfo->child_relid); } /* @@ -1335,7 +1348,6 @@ inheritance_planner(PlannerInfo *root) */ parent_root = parent_roots[appinfo->parent_relid]; Assert(parent_root != NULL); - parent_parse = parent_root->parse; /* * We need a working copy of the PlannerInfo so that we can control @@ -1344,8 +1356,51 @@ inheritance_planner(PlannerInfo *root) subroot = makeNode(PlannerInfo); memcpy(subroot, parent_root, sizeof(PlannerInfo)); - /* grouping_planner doesn't need to see the target children. */ - subroot->append_rel_list = list_copy(orig_append_rel_list); + /* + * Per the above comment, we'll be changing only the AppendRelInfos + * that are contained in orig_append_rel_list, so only copy those. + */ + subroot->append_rel_list = copyObject(orig_append_rel_list); + + /* + * Likewise, for PlanRowMarks. (Fortunately, the executor doesn't + * need to see the modified copies --- we can just pass it the + * original rowMarks list.) + */ + subroot->rowMarks = copyObject(orig_row_marks); + + /* + * No need to copy of the RTEs themselves, but do copy the List + * structure. + */ + subroot->parse->rtable = list_copy(rtable_with_target); + + /* + * If this isn't the first child query, then we can use the child + * objects for source child relations created during the planning of + * 1st child query. IOW, this planning run doesn't need to create the + * child objects again, indicated by setting contains_inherit_children + * sub-PlannerInfo. + */ + if (source_appinfos) + { + subroot->append_rel_list = list_concat(subroot->append_rel_list, + source_appinfos); + /* + * XXX Assert(source_child_rowmarks != NIL); + */ + subroot->rowMarks = list_concat(subroot->rowMarks, + source_child_rowmarks); + Assert(source_child_rtes != NIL); + subroot->parse->rtable = list_concat(subroot->parse->rtable, + source_child_rtes); + + /* + * We have the children, so no need to add them again during this + * planning cycle. + */ + subroot->contains_inherit_children = true; + } /* * Generate modified query with this rel as target. We first apply @@ -1355,7 +1410,7 @@ inheritance_planner(PlannerInfo *root) */ subroot->parse = (Query *) adjust_appendrel_attrs(parent_root, - (Node *) parent_parse, + (Node *) subroot->parse, 1, &appinfo); /* @@ -1421,41 +1476,6 @@ inheritance_planner(PlannerInfo *root) nominalRelation = appinfo->child_relid; /* - * The rowMarks list might contain references to subquery RTEs, so - * make a copy that we can apply ChangeVarNodes to. (Fortunately, the - * executor doesn't need to see the modified copies --- we can just - * pass it the original rowMarks list.) - */ - subroot->rowMarks = copyObject(parent_root->rowMarks); - - /* - * The append_rel_list likewise might contain references to subquery - * RTEs (if any subqueries were flattenable UNION ALLs). So prepare - * to apply ChangeVarNodes to that, too. As explained above, we only - * want to copy items that actually contain such references; the rest - * can just get linked into the subroot's append_rel_list. - * - * If we know there are no such references, we can just use the outer - * append_rel_list unmodified. - */ - if (modifiableARIindexes != NULL) - { - ListCell *lc2; - - subroot->append_rel_list = NIL; - foreach(lc2, parent_root->append_rel_list) - { - AppendRelInfo *appinfo2 = lfirst_node(AppendRelInfo, lc2); - - if (bms_is_member(appinfo2->child_relid, modifiableARIindexes)) - appinfo2 = copyObject(appinfo2); - - subroot->append_rel_list = lappend(subroot->append_rel_list, - appinfo2); - } - } - - /* * Add placeholders to the child Query's rangetable list to fill the * RT indexes already reserved for subqueries in previous children. * These won't be referenced, so there's no need to make them very @@ -1468,23 +1488,23 @@ inheritance_planner(PlannerInfo *root) /* * If this isn't the first child Query, generate duplicates of all * subquery RTEs, and adjust Var numbering to reference the - * duplicates. To simplify the loop logic, we scan the original rtable - * not the copy just made by adjust_appendrel_attrs; that should be OK - * since subquery RTEs couldn't contain any references to the target - * rel. + * duplicates. Note that we scan the original rtable before any + * child target relations were added, which is OK, because no other + * RTEs would contain references to subquery rels being modified. */ if (final_rtable != NIL && subqueryRTindexes != NULL) { ListCell *lr; rti = 1; - foreach(lr, parent_parse->rtable) + foreach(lr, orig_rtable) { RangeTblEntry *rte = lfirst_node(RangeTblEntry, lr); if (bms_is_member(rti, subqueryRTindexes)) { Index newrti; + ListCell *lc2; /* * The RTE can't contain any references to its own RT @@ -1495,19 +1515,21 @@ inheritance_planner(PlannerInfo *root) newrti = list_length(subroot->parse->rtable) + 1; ChangeVarNodes((Node *) subroot->parse, rti, newrti, 0); ChangeVarNodes((Node *) subroot->rowMarks, rti, newrti, 0); - /* Skip processing unchanging parts of append_rel_list */ - if (modifiableARIindexes != NULL) + + /* + * UNION ALL related appinfos are the beginning of the + * list, only change those. + */ + foreach(lc2, subroot->append_rel_list) { - ListCell *lc2; + AppendRelInfo *appinfo2 = lfirst_node(AppendRelInfo, + lc2); - foreach(lc2, subroot->append_rel_list) - { - AppendRelInfo *appinfo2 = lfirst_node(AppendRelInfo, lc2); - - if (bms_is_member(appinfo2->child_relid, - modifiableARIindexes)) - ChangeVarNodes((Node *) appinfo2, rti, newrti, 0); - } + if (bms_is_member(appinfo2->child_relid, + modifiableARIindexes)) + ChangeVarNodes((Node *) appinfo2, rti, newrti, 0); + else + break; } rte = copyObject(rte); ChangeVarNodes((Node *) rte->securityQuals, rti, newrti, 0); @@ -1536,6 +1558,54 @@ inheritance_planner(PlannerInfo *root) subpath = sub_final_rel->cheapest_total_path; /* + * If we finished planning our first child query, copy the source + * child objects that were added during its planning. + */ + if (source_appinfos == NIL && subroot->append_rel_list) + { + int num_skip_appinfos = list_length(orig_append_rel_list); + int num_skip_rowmarks = list_length(orig_row_marks); + int num_skip_rtes = list_length(rtable_with_target); + ListCell *lc2; + + source_appinfos = list_copy_tail(subroot->append_rel_list, + num_skip_appinfos); + Assert(source_child_rowmarks == NIL); + source_child_rowmarks = list_copy_tail(subroot->rowMarks, + num_skip_rowmarks); + Assert(source_child_rtes == NIL); + source_child_rtes = list_copy_tail(subroot->parse->rtable, + num_skip_rtes); + + /* + * Original parent PlanRowMark is modified when adding the + * child PlanRowMarks. Copy those changes so that the planning + * of subsequent child queries works correctly. That is + * necessary, because we won't be adding child objects again, + * so there won't be an opportunity to modify the parent + * PlanRowMark as desired. + * + * All the original parent row marks are the beginning of + * subroot->rowMarks, skip the rest. + */ + foreach(lc2, orig_row_marks) + { + PlanRowMark *oldrc = lfirst_node(PlanRowMark, lc2); + ListCell *lc3; + + foreach(lc3, subroot->rowMarks) + { + PlanRowMark *newrc = lfirst_node(PlanRowMark, lc3); + + if (oldrc->rti == newrc->rti) + oldrc->allMarkTypes = newrc->allMarkTypes; + else + break; + } + } + } + + /* * If this child rel was excluded by constraint exclusion, exclude it * from the result plan. */ diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 71942394ba..30946f77b6 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -2559,16 +2559,16 @@ update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a; Index Cond: (a = 1) -> Nested Loop (actual rows=1 loops=1) -> Append (actual rows=1 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_a1_b1_2 (actual rows=0 loops=1) + -> Bitmap Heap Scan on ab_a1_b1 ab_a1_b1_1 (actual rows=0 loops=1) Recheck Cond: (a = 1) -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b2 ab_a1_b2_2 (actual rows=1 loops=1) + -> Bitmap Heap Scan on ab_a1_b2 ab_a1_b2_1 (actual rows=1 loops=1) Recheck Cond: (a = 1) Heap Blocks: exact=1 -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1) Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b3 ab_a1_b3_2 (actual rows=0 loops=1) + -> Bitmap Heap Scan on ab_a1_b3 ab_a1_b3_1 (actual rows=0 loops=1) Recheck Cond: (a = 1) -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1) Index Cond: (a = 1) @@ -2580,16 +2580,16 @@ update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a; Index Cond: (a = 1) -> Nested Loop (actual rows=0 loops=1) -> Append (actual rows=1 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_a1_b1_3 (actual rows=0 loops=1) + -> Bitmap Heap Scan on ab_a1_b1 ab_a1_b1_1 (actual rows=0 loops=1) Recheck Cond: (a = 1) -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b2 ab_a1_b2_3 (actual rows=1 loops=1) + -> Bitmap Heap Scan on ab_a1_b2 ab_a1_b2_1 (actual rows=1 loops=1) Recheck Cond: (a = 1) Heap Blocks: exact=1 -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1) Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b3 ab_a1_b3_3 (actual rows=0 loops=1) + -> Bitmap Heap Scan on ab_a1_b3 ab_a1_b3_1 (actual rows=0 loops=1) Recheck Cond: (a = 1) -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1) Index Cond: (a = 1) diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index a6a499ed4a..2e170497c9 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -1815,26 +1815,26 @@ AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; -> Seq Scan on t3 t1_2_2 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) -> Nested Loop - Join Filter: (t1_1_1.b = t1_2_3.b) + Join Filter: (t1_1_1.b = t1_2.b) -> Seq Scan on t2 t1_1_1 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) -> Append - -> Seq Scan on t1 t1_2_3 + -> Seq Scan on t1 t1_2 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2_4 + -> Seq Scan on t2 t1_2_1 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2_5 + -> Seq Scan on t3 t1_2_2 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) -> Nested Loop - Join Filter: (t1_1_2.b = t1_2_6.b) + Join Filter: (t1_1_2.b = t1_2.b) -> Seq Scan on t3 t1_1_2 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) -> Append - -> Seq Scan on t1 t1_2_6 + -> Seq Scan on t1 t1_2 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2_7 + -> Seq Scan on t2 t1_2_1 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2_8 + -> Seq Scan on t3 t1_2_2 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) (37 rows) -- 2.11.0