From b43e2c05867c6c3b13095c72409a87ba51733611 Mon Sep 17 00:00:00 2001 From: amit Date: Mon, 4 Mar 2019 15:52:11 +0900 Subject: [PATCH v35 7/7] 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 won'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. --- contrib/postgres_fdw/expected/postgres_fdw.out | 46 +++++------ src/backend/nodes/outfuncs.c | 1 + src/backend/optimizer/plan/planner.c | 103 ++++++++++++++++++++++++- src/backend/optimizer/util/relnode.c | 61 +++++++++++++-- src/include/nodes/pathnodes.h | 7 ++ src/test/regress/expected/partition_prune.out | 66 ++++++++++++++-- src/test/regress/expected/rowsecurity.out | 16 ++-- src/test/regress/sql/partition_prune.sql | 7 ++ 8 files changed, 263 insertions(+), 44 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 9b265499cb..7d9ba84dca 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -7190,8 +7190,8 @@ select * from bar where f1 in (select f1 from foo) for share; -- Check UPDATE with inherited target and an inherited source table explain (verbose, costs off) update bar set f2 = f2 + 100 where f1 in (select f1 from foo); - QUERY PLAN ---------------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------- Update on public.bar Update on public.bar Foreign Update on public.bar2 @@ -7214,22 +7214,22 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo); Output: foo2.ctid, foo2.f1, foo2.*, foo2.tableoid Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1 -> Hash Join - Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, foo_1.ctid, foo_1.*, foo_1.tableoid + Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, foo.ctid, foo.*, foo.tableoid Inner Unique: true - Hash Cond: (bar2.f1 = foo_1.f1) + Hash Cond: (bar2.f1 = foo.f1) -> Foreign Scan on public.bar2 Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE -> Hash - Output: foo_1.ctid, foo_1.f1, foo_1.*, foo_1.tableoid + Output: foo.ctid, foo.*, foo.tableoid, foo.f1 -> HashAggregate - Output: foo_1.ctid, foo_1.f1, foo_1.*, foo_1.tableoid - Group Key: foo_1.f1 + Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Group Key: foo.f1 -> Append - -> Seq Scan on public.foo foo_1 - Output: foo_1.ctid, foo_1.f1, foo_1.*, foo_1.tableoid - -> Foreign Scan on public.foo2 foo2_1 - Output: foo2_1.ctid, foo2_1.f1, foo2_1.*, foo2_1.tableoid + -> Seq Scan on public.foo + Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + -> Foreign Scan on public.foo2 + Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1 Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1 (39 rows) @@ -7276,8 +7276,8 @@ where bar.f1 = ss.f1; -> Seq Scan on public.bar Output: bar.f1, bar.f2, bar.ctid -> Merge Join - Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, (ROW(foo_2.f1)) - Merge Cond: (bar2.f1 = foo_2.f1) + Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, (ROW(foo.f1)) + Merge Cond: (bar2.f1 = foo.f1) -> Sort Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid Sort Key: bar2.f1 @@ -7285,18 +7285,18 @@ where bar.f1 = ss.f1; Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE -> Sort - Output: (ROW(foo_2.f1)), foo_2.f1 - Sort Key: foo_2.f1 + Output: (ROW(foo.f1)), foo.f1 + Sort Key: foo.f1 -> Append - -> Seq Scan on public.foo foo_2 - Output: ROW(foo_2.f1), foo_2.f1 - -> Foreign Scan on public.foo2 foo2_2 - Output: ROW(foo2_2.f1), foo2_2.f1 + -> Seq Scan on public.foo + Output: ROW(foo.f1), foo.f1 + -> Foreign Scan on public.foo2 + Output: ROW(foo2.f1), foo2.f1 Remote SQL: SELECT f1 FROM public.loct1 - -> Seq Scan on public.foo foo_3 - Output: ROW((foo_3.f1 + 3)), (foo_3.f1 + 3) - -> Foreign Scan on public.foo2 foo2_3 - Output: ROW((foo2_3.f1 + 3)), (foo2_3.f1 + 3) + -> Seq Scan on public.foo foo_1 + Output: ROW((foo_1.f1 + 3)), (foo_1.f1 + 3) + -> Foreign Scan on public.foo2 foo2_1 + Output: ROW((foo2_1.f1 + 3)), (foo2_1.f1 + 3) Remote SQL: SELECT f1 FROM public.loct1 (45 rows) diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 910a738c20..1add6c4193 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2227,6 +2227,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node) WRITE_BITMAPSET_FIELD(curOuterRels); WRITE_NODE_FIELD(curOuterParams); WRITE_BOOL_FIELD(partColsUpdated); + WRITE_BOOL_FIELD(contains_inherit_children); } static void diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 92f506fef9..b5c1a4b5ed 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -637,6 +637,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, root->wt_param_id = -1; root->non_recursive_path = NULL; root->partColsUpdated = false; + root->contains_inherit_children = false; /* * If there is a WITH list, process each WITH query and either convert it @@ -1210,6 +1211,10 @@ inheritance_planner(PlannerInfo *root) PlannerInfo **parent_roots = NULL; List *orig_rtable = list_copy(root->parse->rtable); List *orig_append_rel_list = list_copy(root->append_rel_list); + List *source_appinfos = NIL; + List *source_child_rowmarks = NIL; + List *source_child_rtes = NIL; + bool is_first_child; Assert(parse->commandType != CMD_INSERT); @@ -1217,7 +1222,8 @@ inheritance_planner(PlannerInfo *root) * Add child target relations. Note that this only adds the children of * the query's target relation and no other. Especially, the children of * any source relations are added by query_planner() during the planning - * of each child query. + * of the 1st child query and reused for the planning of subsequent child + * queries. */ parent_rte = rt_fetch(top_parentRTindex, root->parse->rtable); @@ -1296,6 +1302,7 @@ inheritance_planner(PlannerInfo *root) /* * And now we can get on with generating a plan for each child table. */ + is_first_child = true; foreach(lc, root->append_rel_list) { AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc); @@ -1435,6 +1442,36 @@ inheritance_planner(PlannerInfo *root) subroot->rowMarks = copyObject(parent_root->rowMarks); /* + * 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 + * of the child PlannerInfo. We use list_copy() on source_child_rtes, + * because we may add more entries to subroot->parse->rtable below. + * + * We don't need to add source_appinfos into subroot yet, because we + * won't need to modify any of its entries. Only orig_append_rel_list + * will contain references to the subquery RTEs that we've already + * made copies of. This also saves time below in the + * ChangeVarNodes((Node *) subroot->append_rel_list, ...) statement. + */ + if (!is_first_child) + { + subroot->rowMarks = + list_concat(subroot->rowMarks, + list_copy(source_child_rowmarks)); + subroot->parse->rtable = + list_concat(subroot->parse->rtable, + list_copy(source_child_rtes)); + + /* + * We have added child RTEs and row marks, so the next + * query_planner doesn't need to add them again. + */ + subroot->contains_inherit_children = true; + } + + /* * 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 @@ -1484,6 +1521,14 @@ inheritance_planner(PlannerInfo *root) } } + /* + * Concatenate source AppendRelInfos, so the source child objects that + * we hooked into subroot above are discoverable. + */ + if (!is_first_child) + subroot->append_rel_list = list_concat(subroot->append_rel_list, + source_appinfos); + /* There shouldn't be any OJ info to translate, as yet */ Assert(subroot->join_info_list == NIL); /* and we haven't created PlaceHolderInfos, either */ @@ -1502,6 +1547,62 @@ inheritance_planner(PlannerInfo *root) subpath = sub_final_rel->cheapest_total_path; /* + * If we just finished planning the first child query, record the + * child objects of source inheritance sets that were generated + * during planning, if any. + */ + if (is_first_child && subroot->append_rel_list) + { + int n_skip_appinfos = list_length(orig_append_rel_list); + int n_skip_rowmarks = list_length(parent_root->rowMarks); + int n_skip_rtes = list_length(parent_parse->rtable); + ListCell *lc2; + + Assert(source_appinfos == NIL); + source_appinfos = list_copy_tail(subroot->append_rel_list, + n_skip_appinfos); + Assert(source_child_rowmarks == NIL); + source_child_rowmarks = list_copy_tail(subroot->rowMarks, + n_skip_rowmarks); + Assert(source_child_rtes == NIL); + source_child_rtes = list_copy_tail(subroot->parse->rtable, + n_skip_rtes); + + /* + * Parent PlanRowMarks (actually their copies in the child + * subroot) are modified when adding the child PlanRowMarks. + * Copy those changes back into the parent_root's copies of those + * parent PlanRowMarks. Doing that is necessary, because we won't + * hit the code that adds child PlanRowMarks again. + * + * The original parent row marks are the beginning of + * subroot->rowMarks; skip the rest. + */ + foreach(lc2, parent_root->rowMarks) + { + PlanRowMark *oldrc = lfirst_node(PlanRowMark, lc2); + ListCell *lc3; + + foreach(lc3, subroot->rowMarks) + { + PlanRowMark *newrc = lfirst_node(PlanRowMark, lc3); + + if (oldrc->rti == newrc->rti) + { + /* modified in expand_single_inheritance_child() */ + oldrc->allMarkTypes = newrc->allMarkTypes; + /* modified in expand_inherited_rtentry() */ + oldrc->isParent = newrc->isParent; + } + else + break; + } + } + + is_first_child = false; + } + + /* * If this child rel was excluded by constraint exclusion, exclude it * from the result plan. */ diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 7f3d66512c..8d04922da2 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -494,26 +494,50 @@ add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, Index rti) { ListCell *l; RelOptInfo *rel; + Relation relation = NULL; + PartitionDesc partdesc = NULL; + int i; /* - * Add inheritance children to the query. For child tables that are - * themselves partitioned, their children will be added recursively. + * Add inheritance children to the query if not already done. For child + * tables that are themselves partitioned, their children will be added + * recursively. + * + * If root already contains the children, we just need to build their + * "other rel" RelOptInfos, which we do below. */ - if (rte->rtekind == RTE_RELATION) + if (rte->rtekind == RTE_RELATION && !root->contains_inherit_children) { expand_inherited_rtentry(root, rte, rti); return; } - /* Add other rels of flattened UNION ALL children. */ - Assert(rte->rtekind == RTE_SUBQUERY); + /* Add other rels. */ + Assert(rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_RELATION); rel = find_base_rel(root, rti); + /* + * For partitioned tables, we need to store the child RelOptInfos in the + * rel->part_rels array too. Some elements of this array might remain set + * to NULL if the corresponding partitions were pruned and hence not in + * append_rel_list. + */ + if (rel->part_scheme) + { + Assert(rel->nparts > 0); + rel->part_rels = (RelOptInfo **) + palloc0(sizeof(RelOptInfo *) * rel->nparts); + relation = table_open(rte->relid, NoLock); + partdesc = PartitionDirectoryLookup(root->glob->partition_directory, + relation); + } + foreach(l, root->append_rel_list) { AppendRelInfo *appinfo = lfirst(l); Index childRTindex = appinfo->child_relid; RangeTblEntry *childrte; + RelOptInfo *childrel; if (appinfo->parent_relid != rti) continue; @@ -521,12 +545,37 @@ add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, Index rti) Assert(childRTindex < root->simple_rel_array_size); childrte = root->simple_rte_array[childRTindex]; Assert(childrte != NULL); - (void) build_simple_rel(root, childRTindex, rel); + childrel = build_simple_rel(root, childRTindex, rel); + + /* + * For partitioned parents, we also need to add childrel to its + * part_rels array. The order in which child tables appear in + * append_rel_list is same as the order in which they appear in the + * parent's PartitionDesc, so assigning partitions like this works. + * parent's PartitionDesc. But considering that some partitions may + * have been pruned, we need to scan the PartitionDesc to determine + * the correct position of this childrel. + */ + if (rel->part_scheme != NULL) + { + Assert(partdesc != NULL); + for (i = 0; i < partdesc->nparts; i++) + { + if (childrte->relid == partdesc->oids[i]) + { + rel->part_rels[i] = childrel; + break; + } + } + } /* Child may itself be an inherited relation. */ if (childrte->inh) add_appendrel_other_rels(root, childrte, childRTindex); } + + if (relation) + table_close(relation, NoLock); } /* diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 4809177083..3298bd78ca 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -350,6 +350,13 @@ struct PlannerInfo /* Does this query modify any partition key columns? */ bool partColsUpdated; + + /* + * Does this PlannerInfo and its Query object contain *all* inheritance + * children? If true, the RTEs, the AppendRelInfos, and the PlanRowMarks + * of all the children are assumed to be present. + */ + bool contains_inherit_children; }; diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index a153c4cea7..9930e8b30f 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) @@ -2606,6 +2606,60 @@ table ab; 1 | 3 (1 row) +-- Test UPDATE where source relation has run-time pruning enabled +truncate ab; +insert into ab values (1, 1), (1, 2), (1, 3), (2, 1); +explain (analyze, costs off, summary off, timing off) +update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1); + QUERY PLAN +---------------------------------------------------------------------- + Update on ab_a1 (actual rows=0 loops=1) + Update on ab_a1_b1 + Update on ab_a1_b2 + Update on ab_a1_b3 + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + -> Nested Loop (actual rows=1 loops=1) + -> Seq Scan on ab_a1_b1 (actual rows=1 loops=1) + -> Materialize (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Seq Scan on ab_a2_b1 (actual rows=1 loops=1) + Filter: (b = $0) + -> Seq Scan on ab_a2_b2 (never executed) + Filter: (b = $0) + -> Seq Scan on ab_a2_b3 (never executed) + Filter: (b = $0) + -> Nested Loop (actual rows=1 loops=1) + -> Seq Scan on ab_a1_b2 (actual rows=1 loops=1) + -> Materialize (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Seq Scan on ab_a2_b1 (actual rows=1 loops=1) + Filter: (b = $0) + -> Seq Scan on ab_a2_b2 (never executed) + Filter: (b = $0) + -> Seq Scan on ab_a2_b3 (never executed) + Filter: (b = $0) + -> Nested Loop (actual rows=1 loops=1) + -> Seq Scan on ab_a1_b3 (actual rows=1 loops=1) + -> Materialize (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Seq Scan on ab_a2_b1 (actual rows=1 loops=1) + Filter: (b = $0) + -> Seq Scan on ab_a2_b2 (never executed) + Filter: (b = $0) + -> Seq Scan on ab_a2_b3 (never executed) + Filter: (b = $0) +(36 rows) + +select tableoid::regclass, * from ab; + tableoid | a | b +----------+---+--- + ab_a1_b3 | 1 | 3 + ab_a1_b3 | 1 | 3 + ab_a1_b3 | 1 | 3 + ab_a2_b1 | 2 | 1 +(4 rows) + drop table ab, lprt_a; -- Join create table tbl1(col1 int); diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index ceb5a7673c..4e45b7f3c8 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) diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index a5514c7506..2e4d2b483d 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -588,6 +588,13 @@ explain (analyze, costs off, summary off, timing off) update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a; table ab; +-- Test UPDATE where source relation has run-time pruning enabled +truncate ab; +insert into ab values (1, 1), (1, 2), (1, 3), (2, 1); +explain (analyze, costs off, summary off, timing off) +update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1); +select tableoid::regclass, * from ab; + drop table ab, lprt_a; -- Join -- 2.11.0