From 38c0b8ada53a8c52df89ac59094ca79618b69c9b Mon Sep 17 00:00:00 2001 From: amit Date: Mon, 4 Mar 2019 15:10:29 +0900 Subject: [PATCH v36 3/7] Delay adding inheritance child tables until query_planner Inheritance child tables are now added by add_other_rels_to_query, which is called by query_planner. This replaces expand_inherited_tables called by subquery_planner as the method of adding child tables. At the point when add_other_rels_to_query is called, restriction clauses have already been assigned to individual base relations, so it will now be possible to perform partition pruning so that we know exact child tables to add in that case. This commit however doesn't change when pruning is performed. inheritance_planner now adds the child target tables on its own, because subquery_planner doesn't. Also, since it calls query_planner once for every child relation, source inheritance child tables are added multiple times. This both slows down queries when there are inherited tables in UPDATE/DELETE's FROM clause (source inheritance) and changes the EXPLAIN output a bit because source inheritance child tables' aliases come out different for every targer child subplan. Another patch will fix both these issues. --- contrib/postgres_fdw/expected/postgres_fdw.out | 78 ++++----- src/backend/optimizer/plan/initsplan.c | 11 +- src/backend/optimizer/plan/planner.c | 108 ++++++------ src/backend/optimizer/util/inherit.c | 218 ++++++++++++++++++------- src/backend/optimizer/util/relnode.c | 86 ++++++---- src/include/optimizer/inherit.h | 4 +- src/include/optimizer/pathnode.h | 1 + src/test/regress/expected/partition_prune.out | 12 +- src/test/regress/expected/rowsecurity.out | 16 +- 9 files changed, 334 insertions(+), 200 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 2be67bca02..f6f8b0f99e 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -7116,9 +7116,9 @@ select * from bar where f1 in (select f1 from foo) for update; QUERY PLAN ---------------------------------------------------------------------------------------------- LockRows - Output: bar.f1, bar.f2, bar.ctid, bar.*, bar.tableoid, foo.ctid, foo.*, foo.tableoid + Output: bar.f1, bar.f2, bar.ctid, foo.ctid, bar.*, bar.tableoid, foo.*, foo.tableoid -> Hash Join - Output: bar.f1, bar.f2, bar.ctid, bar.*, bar.tableoid, foo.ctid, foo.*, foo.tableoid + Output: bar.f1, bar.f2, bar.ctid, foo.ctid, bar.*, bar.tableoid, foo.*, foo.tableoid Inner Unique: true Hash Cond: (bar.f1 = foo.f1) -> Append @@ -7128,15 +7128,15 @@ select * from bar where f1 in (select f1 from foo) for update; Output: bar2.f1, bar2.f2, bar2.ctid, bar2.*, bar2.tableoid Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE -> Hash - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo.ctid, foo.f1, foo.*, foo.tableoid -> HashAggregate - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo.ctid, foo.f1, foo.*, foo.tableoid Group Key: foo.f1 -> Append -> Seq Scan on public.foo - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo.ctid, foo.f1, foo.*, foo.tableoid -> Foreign Scan on public.foo2 - Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1 + Output: foo2.ctid, foo2.f1, foo2.*, foo2.tableoid Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1 (23 rows) @@ -7154,9 +7154,9 @@ select * from bar where f1 in (select f1 from foo) for share; QUERY PLAN ---------------------------------------------------------------------------------------------- LockRows - Output: bar.f1, bar.f2, bar.ctid, bar.*, bar.tableoid, foo.ctid, foo.*, foo.tableoid + Output: bar.f1, bar.f2, bar.ctid, foo.ctid, bar.*, bar.tableoid, foo.*, foo.tableoid -> Hash Join - Output: bar.f1, bar.f2, bar.ctid, bar.*, bar.tableoid, foo.ctid, foo.*, foo.tableoid + Output: bar.f1, bar.f2, bar.ctid, foo.ctid, bar.*, bar.tableoid, foo.*, foo.tableoid Inner Unique: true Hash Cond: (bar.f1 = foo.f1) -> Append @@ -7166,15 +7166,15 @@ select * from bar where f1 in (select f1 from foo) for share; Output: bar2.f1, bar2.f2, bar2.ctid, bar2.*, bar2.tableoid Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR SHARE -> Hash - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo.ctid, foo.f1, foo.*, foo.tableoid -> HashAggregate - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo.ctid, foo.f1, foo.*, foo.tableoid Group Key: foo.f1 -> Append -> Seq Scan on public.foo - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo.ctid, foo.f1, foo.*, foo.tableoid -> Foreign Scan on public.foo2 - Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1 + Output: foo2.ctid, foo2.f1, foo2.*, foo2.tableoid Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1 (23 rows) @@ -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 @@ -7203,33 +7203,33 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo); -> Seq Scan on public.bar Output: bar.f1, bar.f2, bar.ctid -> Hash - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo.ctid, foo.f1, foo.*, foo.tableoid -> HashAggregate - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo.ctid, foo.f1, foo.*, foo.tableoid Group Key: foo.f1 -> Append -> Seq Scan on public.foo - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo.ctid, foo.f1, foo.*, foo.tableoid -> Foreign Scan on public.foo2 - Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1 + 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.ctid, foo.*, foo.tableoid + Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, foo_1.ctid, foo_1.*, foo_1.tableoid Inner Unique: true - Hash Cond: (bar2.f1 = foo.f1) + Hash Cond: (bar2.f1 = foo_1.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.ctid, foo.*, foo.tableoid, foo.f1 + Output: foo_1.ctid, foo_1.f1, foo_1.*, foo_1.tableoid -> HashAggregate - Output: foo.ctid, foo.*, foo.tableoid, foo.f1 - Group Key: foo.f1 + Output: foo_1.ctid, foo_1.f1, foo_1.*, foo_1.tableoid + Group Key: foo_1.f1 -> Append - -> 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 + -> 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 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.f1)) - Merge Cond: (bar2.f1 = foo.f1) + Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, (ROW(foo_2.f1)) + Merge Cond: (bar2.f1 = foo_2.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.f1)), foo.f1 - Sort Key: foo.f1 + Output: (ROW(foo_2.f1)), foo_2.f1 + Sort Key: foo_2.f1 -> Append - -> Seq Scan on public.foo - Output: ROW(foo.f1), foo.f1 - -> Foreign Scan on public.foo2 - Output: ROW(foo2.f1), foo2.f1 + -> 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 Remote SQL: SELECT f1 FROM public.loct1 - -> 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) + -> 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) Remote SQL: SELECT f1 FROM public.loct1 (45 rows) diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 2068d85024..547cf4d475 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -146,9 +146,16 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode) void add_other_rels_to_query(PlannerInfo *root) { - int rti; + int rti, + orig_simple_rel_array_size; - for (rti = 1; rti < root->simple_rel_array_size; rti++) + /* + * add_appendrel_other_rels() may add entries to the range table. It is + * expected to recursively handle any RTEs that it creates with inh=true. + * So just scan as far as the original end of the rtable list. + */ + orig_simple_rel_array_size = root->simple_rel_array_size; + for (rti = 1; rti < orig_simple_rel_array_size; rti++) { RelOptInfo *rel = root->simple_rel_array[rti]; RangeTblEntry *rte = root->simple_rte_array[rti]; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index e408e77d6f..01b887568c 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -25,6 +25,7 @@ #include "access/table.h" #include "access/xact.h" #include "catalog/pg_constraint.h" +#include "catalog/pg_inherits.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "executor/executor.h" @@ -688,6 +689,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, * remove_useless_result_rtes(). Also check for outer joins --- if none, * we can skip reduce_outer_joins(). And check for LATERAL RTEs, too. * This must be done after we have done pull_up_subqueries(), of course. + * For RTE_RELATION rangetable entries whose inh flag is set, adjust the + * value of the flag by checking whether has_subclass() returns true. */ root->hasJoinRTEs = false; root->hasLateralRTEs = false; @@ -707,29 +710,30 @@ subquery_planner(PlannerGlobal *glob, Query *parse, { hasResultRTEs = true; } + else if (rte->rtekind == RTE_RELATION && rte->inh) + { + rte->inh = has_subclass(rte->relid); + + /* + * While at it, initialize the PartitionDesc infrastructure for + * this query, if not done yet. + */ + if (root->glob->partition_directory == NULL) + root->glob->partition_directory = + CreatePartitionDirectory(CurrentMemoryContext); + } + if (rte->lateral) root->hasLateralRTEs = true; } /* * Preprocess RowMark information. We need to do this after subquery - * pullup (so that all non-inherited RTEs are present) and before - * inheritance expansion (so that the info is available for - * expand_inherited_tables to examine and modify). + * pullup (so that all non-inherited RTEs are present). */ preprocess_rowmarks(root); /* - * Expand any rangetable entries that are inheritance sets into "append - * relations". This can add entries to the rangetable, but they must be - * plain RTE_RELATION entries, so it's OK (and marginally more efficient) - * to do it after checking for joins and other special RTEs. We must do - * this after pulling up subqueries, else we'd fail to handle inherited - * tables in subqueries. - */ - expand_inherited_tables(root); - - /* * Set hasHavingQual to remember if HAVING clause is present. Needed * because preprocess_expression will reduce a constant-true condition to * an empty qual list ... but "HAVING TRUE" is not a semantic no-op. @@ -1205,10 +1209,35 @@ inheritance_planner(PlannerInfo *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); Assert(parse->commandType != CMD_INSERT); /* + * 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. + */ + parent_rte = rt_fetch(top_parentRTindex, root->parse->rtable); + + expand_inherited_rtentry(root, parent_rte, top_parentRTindex); + + /* + * If parent no longer has any children, then treat this as an update of + * a single table. + */ + if (!parent_rte->inh) + { + /* + * We'll be retrieving all tuples to modify, so passing 0.0 for + * tuple_fraction. + */ + grouping_planner(root, false, 0.0); + return; + } + + /* * 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 * controlled by a single ModifyTable node. All the instances share the @@ -1269,7 +1298,6 @@ inheritance_planner(PlannerInfo *root) * not appear anywhere else in the plan, so the confusion explained below * for non-partitioning inheritance cases is not possible. */ - parent_rte = rt_fetch(top_parentRTindex, root->parse->rtable); if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE) { nominalRelation = top_parentRTindex; @@ -1321,6 +1349,15 @@ inheritance_planner(PlannerInfo *root) memcpy(subroot, parent_root, sizeof(PlannerInfo)); /* + * Give all child PlannerInfos the same append_rel_list to begin with, + * especially the one before any target children were added. We + * perform a deep copy, because they will be updated with + * ChangeVarNodes() below. Note that orig_append_rel_list only + * contains appinfos corresponding to flattened UNION ALL subquery. + */ + subroot->append_rel_list = copyObject(orig_append_rel_list); + + /* * Generate modified query with this rel as target. We first apply * adjust_appendrel_attrs, which copies the Query and changes * references to the parent RTE to refer to the current child RTE, @@ -1402,33 +1439,6 @@ inheritance_planner(PlannerInfo *root) 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,20 +1478,8 @@ 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) - { - ListCell *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); - } - } + ChangeVarNodes((Node *) subroot->append_rel_list, rti, + newrti, 0); rte = copyObject(rte); ChangeVarNodes((Node *) rte->securityQuals, rti, newrti, 0); subroot->parse->rtable = lappend(subroot->parse->rtable, diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index b89e8a5f81..f7995a323d 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -18,17 +18,20 @@ #include "access/table.h" #include "catalog/partition.h" #include "catalog/pg_inherits.h" +#include "catalog/pg_type.h" #include "miscadmin.h" +#include "nodes/makefuncs.h" #include "optimizer/appendinfo.h" #include "optimizer/inherit.h" +#include "optimizer/pathnode.h" +#include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/prep.h" +#include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "utils/rel.h" -static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, - Index rti); static void expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, @@ -45,36 +48,6 @@ static Bitmapset *translate_col_privs(const Bitmapset *parent_privs, /* - * expand_inherited_tables - * Expand each rangetable entry that represents an inheritance set - * into an "append relation". At the conclusion of this process, - * the "inh" flag is set in all and only those RTEs that are append - * relation parents. - */ -void -expand_inherited_tables(PlannerInfo *root) -{ - Index nrtes; - Index rti; - ListCell *rl; - - /* - * expand_inherited_rtentry may add RTEs to parse->rtable. The function is - * expected to recursively handle any RTEs that it creates with 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); - for (rti = 1; rti <= nrtes; rti++) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl); - - expand_inherited_rtentry(root, rte, rti); - rl = lnext(rl); - } -} - -/* * expand_inherited_rtentry * Check whether a rangetable entry represents an inheritance set. * If so, add entries for all the child tables to the query's @@ -94,11 +67,12 @@ expand_inherited_tables(PlannerInfo *root) * Since a partitioned table is not scanned, it might have only one associated * AppendRelInfo. */ -static void +void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) { Oid parentOID; PlanRowMark *oldrc; + int old_allMarkTypes; Relation oldrelation; LOCKMODE lockmode; List *inhOIDs; @@ -140,17 +114,16 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) */ oldrc = get_plan_rowmark(root->rowMarks, rti); if (oldrc) + { oldrc->isParent = true; + old_allMarkTypes = oldrc->allMarkTypes; + } /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); - if (root->glob->partition_directory == NULL) - root->glob->partition_directory = - CreatePartitionDirectory(CurrentMemoryContext); - /* * If this table has partitions, recursively expand and lock them. * While at it, also extract the partition key columns of all the @@ -164,6 +137,8 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) List *appinfos = NIL; RangeTblEntry *childrte; Index childRTindex; + bool is_source_inh_expansion; + RelOptInfo *rel = NULL; /* Scan for all members of inheritance set, acquire needed locks */ inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); @@ -182,6 +157,20 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) } /* + * If parent is a source relation of the query, we'd need to add the + * child RelOptInfos as well, in addition to RangeTblEntry's and + * AppendRelInfo's. We can tell it's source relation by noting that + * simple_rel_array has been set up by query_planner. + */ + is_source_inh_expansion = (root->simple_rel_array_size > 0); + if (is_source_inh_expansion) + { + rel = find_base_rel(root, rti); + /* Expand various arrays in PlannerInfo to hold child object. */ + expand_planner_arrays(root, list_length(inhOIDs)); + } + + /* * This table has no partitions. Expand any plain inheritance * children in the order the OIDs were returned by * find_all_inheritors. @@ -214,6 +203,10 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) &appinfos, &childrte, &childRTindex); + /* Create the otherrel RelOptInfo too. */ + if (is_source_inh_expansion) + (void) build_simple_rel(root, childRTindex, rel); + /* Close child relations, but keep locks */ if (childOID != parentOID) table_close(newrelation, NoLock); @@ -234,6 +227,60 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) } + /* + * Some children might require different mark types, which would've been + * reflected in oldrc. If so, add relevant entries to the top-level + * targetlist and update parent rel's reltarget. + */ + if (oldrc) + { + List *tlist = root->processed_tlist; + Var *var; + TargetEntry *tle; + char resname[32]; + List *newvars = NIL; + + /* The old PlanRowMark should already have necessitated adding TID. */ + Assert(oldrc->allMarkTypes & ~(1 << ROW_MARK_COPY)); + + if (oldrc->allMarkTypes != old_allMarkTypes && + oldrc->allMarkTypes & (1 << ROW_MARK_COPY)) + { + /* Need the whole row as a junk var */ + var = makeWholeRowVar(planner_rt_fetch(oldrc->rti, root), + oldrc->rti, + 0, + false); + snprintf(resname, sizeof(resname), "wholerow%u", + oldrc->rowmarkId); + tle = makeTargetEntry((Expr *) var, + list_length(tlist) + 1, + pstrdup(resname), + true); + tlist = lappend(tlist, tle); + newvars = lappend(newvars, var); + } + + /* Always fetch the tableoid too for inheritance parents. */ + Assert(oldrc->isParent); + var = makeVar(oldrc->rti, + TableOidAttributeNumber, + OIDOID, + -1, + InvalidOid, + 0); + snprintf(resname, sizeof(resname), "tableoid%u", oldrc->rowmarkId); + tle = makeTargetEntry((Expr *) var, + list_length(tlist) + 1, + pstrdup(resname), + true); + tlist = lappend(tlist, tle); + newvars = lappend(newvars, var); + + /* Add the newly added Vars to parent's reltarget. */ + add_vars_to_targetlist(root, newvars, bms_make_singleton(0), false); + } + table_close(oldrelation, NoLock); } @@ -251,18 +298,30 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, RangeTblEntry *childrte; Index childRTindex; PartitionDesc partdesc; + RelOptInfo *rel = NULL; + bool is_source_inh_expansion; + + check_stack_depth(); partdesc = PartitionDirectoryLookup(root->glob->partition_directory, parentrel); - check_stack_depth(); - /* A partitioned table should always have a partition descriptor. */ Assert(partdesc); Assert(parentrte->inh); /* + * If the partitioned table has no partitions, treat this as the + * non-inheritance case. + */ + if (partdesc->nparts == 0) + { + parentrte->inh = false; + return; + } + + /* * Note down whether any partition key cols are being updated. Though it's * the root partitioned table's updatedCols we are interested in, we * instead use parentrte to get the updatedCols. This is convenient @@ -274,13 +333,27 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, has_partition_attrs(parentrel, parentrte->updatedCols, NULL); /* - * If the partitioned table has no partitions, treat this as the - * non-inheritance case. + * If parent is a source relation of the query, we'd need to add the child + * RelOptInfos as well, in addition to RangeTblEntry's and + * AppendRelInfo's. We can tell it's source relation by noting that + * simple_rel_array has been set up by query_planner. */ - if (partdesc->nparts == 0) + is_source_inh_expansion = (root->simple_rel_array_size > 0); + if (is_source_inh_expansion && partdesc->nparts > 0) { - parentrte->inh = false; - return; + rel = find_base_rel(root, parentRTindex); + + /* Expand simple_rel_array and friends to hold child objects. */ + expand_planner_arrays(root, partdesc->nparts); + + /* + * We also store partition RelOptInfo pointers in the parent + * relation. + */ + Assert(rel->part_rels == NULL); + if (partdesc->nparts > 0) + rel->part_rels = (RelOptInfo **) + palloc0(rel->nparts * sizeof(RelOptInfo *)); } for (i = 0; i < partdesc->nparts; i++) @@ -303,8 +376,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, parentrel, top_parentrc, childrel, appinfos, &childrte, &childRTindex); - /* If this child is itself partitioned, recurse */ - if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + /* Create the otherrel RelOptInfo too. */ + if (is_source_inh_expansion) + { + Assert(rel->part_rels != NULL && rel->part_rels[i] == NULL); + rel->part_rels[i] = build_simple_rel(root, childRTindex, rel); + } + + /* + * If this child is itself partitioned, recurse. Back off if the inh + * flag was reset by expand_single_inheritance_child(). + */ + if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && + childrte->inh) expand_partitioned_rtentry(root, childrte, childRTindex, childrel, top_parentrc, lockmode, appinfos); @@ -347,7 +431,14 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; - AppendRelInfo *appinfo; + AppendRelInfo *appinfo = NULL; + PartitionDesc child_partdesc = NULL; + bool is_source_inh_expansion; + + if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + child_partdesc = + PartitionDirectoryLookup(root->glob->partition_directory, + childrel); /* * Build an RTE for the child, and attach to query's rangetable list. We @@ -365,9 +456,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, *childrte_p = childrte; childrte->relid = childOID; childrte->relkind = childrel->rd_rel->relkind; - /* A partitioned child will need to be expanded further. */ - if (childOID != parentOID && - childrte->relkind == RELKIND_PARTITIONED_TABLE) + /* + * A partitioned child will need to be expanded further, but only if it + * actually has partitions. + */ + if (childOID != parentOID && child_partdesc && child_partdesc->nparts > 0) childrte->inh = true; else childrte->inh = false; @@ -378,13 +471,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, *childRTindex_p = childRTindex; /* - * We need an AppendRelInfo if paths will be built for the child RTE. If - * childrte->inh is true, then we'll always need to generate append paths - * for it. If childrte->inh is false, we must scan it if it's not a - * partitioned table; but if it is a partitioned table, then it never has - * any data of its own and need not be scanned. + * Don't need an AppendRelInfo for duplicate RTEs we create for + * partitioned table parent rels. */ - if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh) + if (childrte->relid != parentOID || + childrte->relkind != RELKIND_PARTITIONED_TABLE) { appinfo = make_append_rel_info(parentrel, childrel, parentRTindex, childRTindex); @@ -411,6 +502,21 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } /* + * If we are expanding source inheritance, store the RTE and appinfo in the + * respective PlannerInfo arrays, which the caller must already have + * allocated space for. + */ + is_source_inh_expansion = (root->simple_rel_array_size > 0); + if (is_source_inh_expansion > 0) + { + Assert(childRTindex < root->simple_rel_array_size); + Assert(root->simple_rte_array[childRTindex] == NULL); + root->simple_rte_array[childRTindex] = childrte; + Assert(root->append_rel_array[childRTindex] == NULL); + root->append_rel_array[childRTindex] = appinfo; + } + + /* * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. */ if (top_parentrc) diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 8a59819469..c64d6b72cc 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -139,6 +139,50 @@ setup_append_rel_array(PlannerInfo *root) } /* + * expand_planner_arrays + * Expand the PlannerInfo arrays by add_size members and initialize the + * the newly added bytes with zero + */ +void +expand_planner_arrays(PlannerInfo *root, int add_size) +{ + int new_size; + + Assert(add_size > 0); + + new_size = root->simple_rel_array_size + add_size; + + /* Expand various arrays and 0-initialize added bytes. */ + root->simple_rte_array = (RangeTblEntry **) + repalloc(root->simple_rte_array, + sizeof(RangeTblEntry *) * new_size); + MemSet(root->simple_rte_array + root->simple_rel_array_size, + 0, sizeof(RangeTblEntry *) * add_size); + root->simple_rel_array = (RelOptInfo **) + repalloc(root->simple_rel_array, + sizeof(RelOptInfo *) * new_size); + MemSet(root->simple_rel_array + root->simple_rel_array_size, + 0, sizeof(RelOptInfo *) * add_size); + + if (root->append_rel_array) + { + root->append_rel_array = (AppendRelInfo **) + repalloc(root->append_rel_array, + sizeof(AppendRelInfo *) * new_size); + MemSet(root->append_rel_array + root->simple_rel_array_size, + 0, sizeof(AppendRelInfo *) * add_size); + } + else + { + root->append_rel_array = (AppendRelInfo **) + palloc0(sizeof(AppendRelInfo *) * + new_size); + } + + root->simple_rel_array_size = new_size; +} + +/* * build_simple_rel * Construct a new RelOptInfo for a base relation or 'other' relation. */ @@ -295,28 +339,26 @@ add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, Index rti) { ListCell *l; RelOptInfo *rel; - int i; - - rel = find_base_rel(root, rti); /* - * For partitioned tables, we need to store the child RelOptInfos in the - * rel->part_rels array too. + * Add inheritance children to the query. For child tables that are + * themselves partitioned, their children will be added recursively. */ - if (rel->part_scheme) + if (rte->rtekind == RTE_RELATION) { - Assert(rel->nparts > 0); - rel->part_rels = (RelOptInfo **) - palloc0(sizeof(RelOptInfo *) * rel->nparts); + expand_inherited_rtentry(root, rte, rti); + return; } - i = 0; + /* Add other rels of flattened UNION ALL children. */ + Assert(rte->rtekind == RTE_SUBQUERY); + rel = find_base_rel(root, rti); + 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; @@ -324,32 +366,12 @@ 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); - 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. - */ - if (rel->part_scheme != NULL) - { - Assert(i < rel->nparts); - rel->part_rels[i] = childrel; - } - - i++; + (void) build_simple_rel(root, childRTindex, rel); /* Child may itself be an inherited relation. */ if (childrte->inh) add_appendrel_other_rels(root, childrte, childRTindex); } - - /* - * For a partitioned table with non-zero number of partitions, we must - * have assigned all elements of its part_rels array. - */ - Assert(rel->nparts == 0 || i == rel->nparts); } /* diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index d2418f15cf..427c1d0abe 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -16,7 +16,7 @@ #include "nodes/pathnodes.h" - -extern void expand_inherited_tables(PlannerInfo *root); +extern void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, + Index rti); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 1a07963a7d..60361507d2 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -277,6 +277,7 @@ extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path, */ extern void setup_simple_rel_arrays(PlannerInfo *root); extern void setup_append_rel_array(PlannerInfo *root); +extern void expand_planner_arrays(PlannerInfo *root, int add_size); extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent); extern void add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 50ca03b9e3..61f4d8cab2 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -2521,16 +2521,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_1 (actual rows=0 loops=1) + -> Bitmap Heap Scan on ab_a1_b1 ab_a1_b1_2 (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_1 (actual rows=1 loops=1) + -> Bitmap Heap Scan on ab_a1_b2 ab_a1_b2_2 (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_1 (actual rows=0 loops=1) + -> Bitmap Heap Scan on ab_a1_b3 ab_a1_b3_2 (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) @@ -2542,16 +2542,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_1 (actual rows=0 loops=1) + -> Bitmap Heap Scan on ab_a1_b1 ab_a1_b1_3 (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_1 (actual rows=1 loops=1) + -> Bitmap Heap Scan on ab_a1_b2 ab_a1_b2_3 (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_1 (actual rows=0 loops=1) + -> Bitmap Heap Scan on ab_a1_b3 ab_a1_b3_3 (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 4b53401b00..420b048976 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -1813,26 +1813,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.b) + Join Filter: (t1_1_1.b = t1_2_3.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 + -> Seq Scan on t1 t1_2_3 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2_1 + -> Seq Scan on t2 t1_2_4 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2_2 + -> Seq Scan on t3 t1_2_5 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) -> Nested Loop - Join Filter: (t1_1_2.b = t1_2.b) + Join Filter: (t1_1_2.b = t1_2_6.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 + -> Seq Scan on t1 t1_2_6 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2_1 + -> Seq Scan on t2 t1_2_7 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2_2 + -> Seq Scan on t3 t1_2_8 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) (37 rows) -- 2.11.0