From: Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
To: Alvaro Herrera <alvherre@2ndquadrant.com>
Cc: Imai, Yoshikazu <imai.yoshikazu@jp.fujitsu.com>
Cc: Pg Hackers <pgsql-hackers@postgresql.org>
Cc: David Rowley <david.rowley@2ndquadrant.com>
Subject: Re: speeding up planning with partitions
Date: Fri, 9 Nov 2018 17:55:05 +0900
Message-ID: <3be67028-a00a-502c-199a-da00eec8fb6e@lab.ntt.co.jp> (raw)
In-Reply-To: <f74d2f85-a9c3-f82d-188a-fcfe77911e78@lab.ntt.co.jp>
References: <20181107033207.cdisp33ryrgeidef@alvherre.pgsql>
<f74d2f85-a9c3-f82d-188a-fcfe77911e78@lab.ntt.co.jp>
On 2018/11/07 12:44, Amit Langote wrote:
> Thanks for looking.
>
> On 2018/11/07 12:32, Alvaro Herrera wrote:
>> ISTM patch 0004 is impossible to review just because of size -- I
>> suppose the bulk of it is just code that moves from one file to another,
>> but that there are also code changes in it. Maybe it would be better to
>> split it so that one patch moves the code to the new files without
>> changing it, then another patch does the actual functional changes? If
>> git shows the first half as a rename, it's easier to be confident about
>> it.
>
> Okay, will post a new version structured that way shortly.
And here are patches structured that way. I've addressed some of the
comments posted by Imai-san upthread. Also, since David's patch to
postpone PlannerInfo.total_pages calculation went into the tree earlier
this week, it's no longer included in this set.
With this breakdown, patches are as follows:
v5-0001-Store-inheritance-root-parent-index-in-otherrel-s.patch
Adds a inh_root_parent field that's set in inheritance child otherrel
RelOptInfos to store the RT index of their root parent
v5-0002-Overhaul-inheritance-update-delete-planning.patch
Patch that adjusts planner so that inheritance_planner can use partition
pruning.
v5-0003-Lazy-creation-of-RTEs-for-inheritance-children.patch
Patch that adjusts planner such that inheritance expansion step is
postponed from early subquery_planner to the beginning of
set_append_rel_size, so that child tables are added to the Query and
PlannerInfo after partition pruning. While the move only benefits
partitioning, because non-partition children cannot be pruned until
after they're fully opened, the new code handles both partitioned tables
and regular inheritance parents.
v5-0004-Move-append-expansion-code-into-its-own-file.patch
With patch 0003, inheritance expansion is no longer a part of the prep
phase of planning, so it seems odd that inheritance expansion code is in
optimizer/prep/prepunion.c. This patch moves the code to a new file
optimizer/utils/append.c. Also, some of the appendrel translation
subroutines are moved over to optimizer/utils/appendinfo.c.
No functional changes with this patch.
v5-0005-Teach-planner-to-only-process-unpruned-partitions.patch
Patch that adds a live_parts field to RelOptInfo which is set in
partitioned rels to contain partition indexes of only non-dummy children
replace the loops of the following form:
for (i = 0; i < rel->nparts; i++)
{
RelOptInfo *partrel = rel->part_rels[i];
...some processing
}
at various points within the planner with:
i = -1
while ((i = bms_get_next(rel->live_parts)) >= 0)
{
RelOptInfo *partrel = rel->part_rels[i];
...some processing
}
v5-0006-Do-not-lock-all-partitions-at-the-beginning.patch
A simple patch that removes the find_all_inheritors called for
partitioned root parent only to lock *all* partitions, in favor of
locking only the unpruned partitions.
Thanks,
Amit
From 0f66b82a6ab998c4d790a4e2a6437950edf7de18 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 26 Oct 2018 16:45:59 +0900
Subject: [PATCH v5 1/6] Store inheritance root parent index in otherrel's
RelOptInfo
Although it's set by build_simple_rel, it's not being used by any
code yet.
---
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/util/relnode.c | 14 ++++++++++++++
src/include/nodes/relation.h | 4 ++++
3 files changed, 19 insertions(+)
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.cindex 69731ccdea..53a657c0ae 100644--- a/src/backend/nodes/outfuncs.c+++ b/src/backend/nodes/outfuncs.c@@ -2371,6 +2371,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
WRITE_BOOL_FIELD(consider_partitionwise_join);
WRITE_BITMAPSET_FIELD(top_parent_relids);
WRITE_NODE_FIELD(partitioned_child_rels);
+ WRITE_UINT_FIELD(inh_root_parent);
}
static void
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.cindex 39f5729b91..29ba19349f 100644--- a/src/backend/optimizer/util/relnode.c+++ b/src/backend/optimizer/util/relnode.c@@ -215,9 +215,23 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->top_parent_relids = parent->top_parent_relids;
else
rel->top_parent_relids = bms_copy(parent->relids);
++ /*+ * For inheritance child relations, we also set inh_root_parent.+ * Note that 'parent' might itself be a child (a sub-partitioned+ * partition), in which case we simply use its value of+ * inh_root_parent.+ */+ if (parent->rtekind == RTE_RELATION)+ rel->inh_root_parent = parent->inh_root_parent > 0 ?+ parent->inh_root_parent :+ parent->relid;
}
else
+ {
rel->top_parent_relids = NULL;
+ rel->inh_root_parent = 0;+ }
/* Check type of rtable entry */
switch (rte->rtekind)
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.hindex 6fd24203dd..32ac3315a4 100644--- a/src/include/nodes/relation.h+++ b/src/include/nodes/relation.h@@ -703,6 +703,10 @@ typedef struct RelOptInfo
List **partexprs; /* Non-nullable partition key expressions. */
List **nullable_partexprs; /* Nullable partition key expressions. */
List *partitioned_child_rels; /* List of RT indexes. */
++ Index inh_root_parent; /* For otherrels, this is the RT index of+ * inheritance table mentioned in the query+ * from which this relation originated */
} RelOptInfo;
/*
--
2.11.0
From f14e09f119ce85c344b581af6b1623982d7cc2b1 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 17 Oct 2018 11:18:12 +0900
Subject: [PATCH v5 2/6] Overhaul inheritance update/delete planning
With the current method, inheritance_planner, which handles the
planning for update/delete commands targeting inheritance trees,
performs the *whole* planning for each child table. That involves
translating the query tree to set a given child table in place of
the original inheritance root table as the query's target table,
followed by calling grouping_planner on the translated query.
That's inefficient for two reasons:
1. partprune.c cannot be used to perform partition pruning, because
it can only be invoked from query_planner. With the current
approach, query_planner only sees the individual partitions in
the query tree it receives, not the parent partitioned table.
This leaves each partition to be pruned using constraint
exclusion.
2. Repeated invocation of query_planner results in allocating large
amounts of memory, especially if there are many child tables.
Also, it involves repeatedly performing the same processing,
such jointree processing.
This commit addresses both of the issues by rewriting portions of
inheritance_planner and adding some supporting functionality to
allpath.c.
With the new implementation, inheritance_planner calls query_planner
only once at the beginning with the original unmodified query, which
creates the access paths of individual relations after pruning any
unnecessary partitions. If the query involves join against the target
relation, join paths are created for each target child relation by
replacing the original target table in the join tree by a given child
table. Join relations (RelOptInfos thereof) for all target child
relations are collected in a global list in PlannerInfo.
After creating the join paths for all target child relations, it
calls grouping_planner() on each child join relation to finish up the
paths such that they produce query's top-level target list expanded
per a given child relation's descriptor. grouping_planner()'s
interface is modified so that we can pass it what's called a
'planned_rel', a RelOptInfo that already contains the necessary
paths needed to produce its output.
This removes some existing code in inheritance_planner that dealt
with any subquery RTEs in the query. The rationale of that code
was that the subquery RTEs may change during each iteration of
planning (that is, for different children), so different iterations
better use different copies of those RTEs. That was handled by
making fresh copies of those RTEs for each iteration of planning
which were appended to the range table, accompanied by modifying all
expressions and auxiliary structures that referenced the original
subquery RTEs to instead reference the copies (that is, change the
varnos). This copying meant we would end up adding S * N new entries
to the original range table by the time we got to the last unpruned
child, where S is the number of subquery RTEs in the original query
and N the number of unpruned children. Since with the new code
we perform planning just once, I think we don't need this special
handling. Actually, there is a regression test output change due
to no longer having copies of subquery RTEs (see the diff of
partition_join.out file.)
Also we no longer need the hack in relation_excluded_by_constraints()
involving setting PlannerInfo.inhTargetKind to enable constraint
exclusion for target child relation is no longer needed.
This commit removes inheritance_planner in favor of rearranging
things such that, only the planning steps that affect 1 and 2 above
are repeated for unpruned child relations.
---
doc/src/sgml/ddl.sgml | 14 +-
src/backend/nodes/outfuncs.c | 1 -
src/backend/optimizer/path/allpaths.c | 212 +++++++++++++-
src/backend/optimizer/plan/createplan.c | 10 -
src/backend/optimizer/plan/planner.c | 400 +++++++--------------------
src/backend/optimizer/prep/prepjointree.c | 1 -
src/backend/optimizer/prep/prepunion.c | 33 ++-
src/backend/optimizer/util/plancat.c | 50 +---
src/include/nodes/relation.h | 18 +-
src/test/regress/expected/partition_join.out | 4 +-
10 files changed, 356 insertions(+), 387 deletions(-)
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgmlindex b5ed1b7939..8cb69b1a47 100644--- a/doc/src/sgml/ddl.sgml+++ b/doc/src/sgml/ddl.sgml@@ -3935,15 +3935,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
<note>
<para>
- Currently, pruning of partitions during the planning of an- <command>UPDATE</command> or <command>DELETE</command> command is- implemented using the constraint exclusion method (however, it is- controlled by the <literal>enable_partition_pruning</literal> rather than- <literal>constraint_exclusion</literal>) — see the following section- for details and caveats that apply.- </para>-- <para>
Execution-time partition pruning currently occurs for the
<literal>Append</literal> and <literal>MergeAppend</literal> node types.
</para>
@@ -3964,9 +3955,8 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
<para>
<firstterm>Constraint exclusion</firstterm> is a query optimization
- technique similar to partition pruning. While it is primarily used- for partitioning implemented using the legacy inheritance method, it can be- used for other purposes, including with declarative partitioning.+ technique similar to partition pruning. It is primarily used+ for partitioning implemented using the legacy inheritance method.
</para>
<para>
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.cindex 53a657c0ae..a95153fea7 100644--- a/src/backend/nodes/outfuncs.c+++ b/src/backend/nodes/outfuncs.c@@ -2309,7 +2309,6 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
WRITE_FLOAT_FIELD(limit_tuples, "%.0f");
WRITE_UINT_FIELD(qual_security_level);
- WRITE_ENUM_FIELD(inhTargetKind, InheritanceKind);
WRITE_BOOL_FIELD(hasJoinRTEs);
WRITE_BOOL_FIELD(hasLateralRTEs);
WRITE_BOOL_FIELD(hasDeletedRTEs);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.cindex 738bb30848..8de8d383f9 100644--- a/src/backend/optimizer/path/allpaths.c+++ b/src/backend/optimizer/path/allpaths.c@@ -36,6 +36,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
+#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
@@ -119,6 +120,9 @@ static void set_namedtuplestore_pathlist(PlannerInfo *root, RelOptInfo *rel,
static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
+static void inheritance_make_rel_from_joinlist(PlannerInfo *root,+ RelOptInfo *parent,+ List *joinlist);
static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery,
pushdown_safety_info *safetyInfo);
static bool recurse_pushdown_safe(Node *setOp, Query *topquery,
@@ -217,13 +221,28 @@ make_one_rel(PlannerInfo *root, List *joinlist)
/*
* Generate access paths for the entire join tree.
+ *+ * For UPDATE/DELETE on an inheritance parent, join paths should be+ * generated for each child result rel separately.
*/
- rel = make_rel_from_joinlist(root, joinlist);+ if (root->parse->resultRelation &&+ root->simple_rte_array[root->parse->resultRelation]->inh)+ {+ rel = root->simple_rel_array[root->parse->resultRelation];+ inheritance_make_rel_from_joinlist(root, rel, joinlist);+ Assert(list_length(root->inh_target_child_rels) ==+ list_length(root->inh_target_child_joinrels));+ }+ else+ {+ rel = make_rel_from_joinlist(root, joinlist);- /*- * The result should join all and only the query's base rels.- */- Assert(bms_equal(rel->relids, root->all_baserels));+ /*+ * The result should join all and only the query's base rels.+ */+ Assert(bms_equal(rel->relids, root->all_baserels));++ }
return rel;
}
@@ -1219,6 +1238,63 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
set_rel_consider_parallel(root, childrel, childRTE);
/*
+ * If the root parent is the result relation, we need a reltarget+ * for the child that will be suitable to use the child as+ * target relation.+ */+ if (childrel->inh_root_parent > 0 &&+ childrel->inh_root_parent == root->parse->resultRelation)+ {+ Query *parse,+ *orig_parse = root->parse;+ List *tlist;+ List *other_exprs;+ ListCell *lc2;+ Relids child_relids,+ top_parent_relids;++ /*+ * We'd like to build the reltarget afresh; save the translated+ * version of parent's expressions aside.+ */+ other_exprs = childrel->reltarget->exprs;+ childrel->reltarget->exprs = NIL;++ /*+ * Translate the original query's expressions to this child,+ * mainly for updating the childrel's reltarget so that it+ * contains necessary expressions for using childrel as target+ * relation correctly. We need to use multilevel translation+ * as we can only access the original version of the query.+ */+ child_relids = bms_make_singleton(appinfo->child_relid);+ top_parent_relids = bms_make_singleton(childrel->inh_root_parent);+ parse = (Query *)+ adjust_appendrel_attrs_multilevel(root,+ (Node *) root->parse,+ child_relids,+ top_parent_relids);+ root->parse = parse;+ tlist = preprocess_targetlist(root);+ build_base_rel_tlists(root, tlist);+ root->parse = orig_parse;++ /*+ * Some of the expressions in parent's reltarget might not be+ * in child's just built expressions. Check to add any that are+ * missing.+ */+ foreach(lc2, other_exprs)+ {+ Expr *expr = lfirst(lc2);++ if (!list_member(childrel->reltarget->exprs, expr))+ childrel->reltarget->exprs =+ lappend(childrel->reltarget->exprs, expr);+ }+ }++ /*
* Compute the child's size.
*/
set_rel_size(root, childrel, childRTindex, childRTE);
@@ -2624,6 +2700,132 @@ generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_rows)
}
/*
+ * inheritance_make_rel_from_joinlist+ * Perform join planning for all non-dummy leaf inheritance children+ * in their role as query's target relation+ *+ * If a child relation is a partitioned table, its children are processed in+ * turn by recursively calling this function.+ */+static void+inheritance_make_rel_from_joinlist(PlannerInfo *root,+ RelOptInfo *parent,+ List *joinlist)+{+ List *join_info_list = root->join_info_list;+ ListCell *lc;+#ifdef USE_ASSERT_CHECKING+ int top_parent_relid = parent->inh_root_parent > 0 ?+ parent->inh_root_parent :+ parent->relid;+ Relids all_baserels;+#endif++ /*+ * For UPDATE/DELETE queries, the top parent can only ever be a table.+ * As a contrast, it could be a UNION ALL subquery in the case of SELECT.+ */+ Assert(planner_rt_fetch(top_parent_relid, root)->rtekind == RTE_RELATION);++ /* Nothing to do. */+ if (IS_DUMMY_REL(parent))+ return;++ foreach(lc, root->append_rel_list)+ {+ RelOptInfo *childrel;+ AppendRelInfo *appinfo = lfirst(lc);+ List *translated_joinlist;+ Relids child_relids,+ top_parent_relids;++ /* Only consider this parent's children. */+ if (appinfo->parent_relid != parent->relid)+ continue;++ childrel = find_base_rel(root, appinfo->child_relid);++ /* Ignore excluded/pruned children. */+ if (IS_DUMMY_REL(childrel))+ continue;++ /*+ * Sub-partitions have to be processed recursively, because+ * AppendRelInfos link sub-partitions to their immediate parents, not+ * the root partitioned table.+ */+ if (childrel->part_scheme != NULL)+ {+ inheritance_make_rel_from_joinlist(root, childrel, joinlist);+ continue;+ }++ /*+ * Modify joinlist such that relations joined to the top parent rel+ * appear to be joined to the child rel instead. Do the same for+ * any SpecialJoinInfo structs.+ */+ child_relids = bms_make_singleton(appinfo->child_relid);+ Assert(childrel->inh_root_parent > 0);+ top_parent_relids = bms_make_singleton(childrel->inh_root_parent);+ translated_joinlist = (List *)+ adjust_appendrel_attrs_multilevel(root,+ (Node *) joinlist,+ child_relids,+ top_parent_relids);+ root->join_info_list = (List *)+ adjust_appendrel_attrs_multilevel(root,+ (Node *) join_info_list,+ child_relids,+ top_parent_relids);++ /*+ * Since we added the child rel directly into the join tree, we must+ * modify it to be a "base" rel instead of an "other" rel, which the+ * join planning code expects the relations being joined to be.+ *+ * NB: Do we need to change the child EC members to be marked+ * as non-child somehow?+ */+ childrel->reloptkind = RELOPT_BASEREL;++ /* Reset join planning specific data structures. */+ root->join_rel_list = NIL;+ root->join_rel_hash = NULL;++ /* Perform join planning and save the resulting RelOptInfo. */+ childrel = make_rel_from_joinlist(root, translated_joinlist);++ /*+ * Remember this child target rel. inheritance_planner will perform+ * the remaining steps of planning for each child relation separately.+ * Specifically, it will call grouping_planner on every+ * RelOptInfo contained in the inh_target_child_joinrels list, each+ * of which represents the source of tuples to be modified for a given+ * target child rel.+ */+ root->inh_target_child_rels = lappend_int(root->inh_target_child_rels,+ appinfo->child_relid);+ root->inh_target_child_joinrels =+ lappend(root->inh_target_child_joinrels, childrel);++#ifdef USE_ASSERT_CHECKING+ /*+ * The following implements essentially the same Assert as in+ * make_one_rel, our caller.+ */+ all_baserels = bms_copy(root->all_baserels);+ all_baserels = bms_del_member(all_baserels, top_parent_relid);+ all_baserels = bms_add_member(all_baserels, appinfo->child_relid);+ Assert(bms_equal(childrel->relids, all_baserels));+#endif++ /* Restore the original for processing the next child. */+ root->join_info_list = join_info_list;+ }+}++/*
* make_rel_from_joinlist
* Build access paths using a "joinlist" to guide the join path search.
*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.cindex da7a92081a..28c4b53fea 100644--- a/src/backend/optimizer/plan/createplan.c+++ b/src/backend/optimizer/plan/createplan.c@@ -1998,12 +1998,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
/*
* During setrefs.c, we'll need the grouping_map to fix up the cols lists
* in GroupingFunc nodes. Save it for setrefs.c to use.
- *- * This doesn't work if we're in an inheritance subtree (see notes in- * create_modifytable_plan). Fortunately we can't be because there would- * never be grouping in an UPDATE/DELETE; but let's Assert that.
*/
- Assert(root->inhTargetKind == INHKIND_NONE);
Assert(root->grouping_map == NULL);
root->grouping_map = grouping_map;
@@ -2160,12 +2155,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
* with InitPlan output params. (We can't just do that locally in the
* MinMaxAgg node, because path nodes above here may have Agg references
* as well.) Save the mmaggregates list to tell setrefs.c to do that.
- *- * This doesn't work if we're in an inheritance subtree (see notes in- * create_modifytable_plan). Fortunately we can't be because there would- * never be aggregates in an UPDATE/DELETE; but let's Assert that.
*/
- Assert(root->inhTargetKind == INHKIND_NONE);
Assert(root->minmax_aggs == NIL);
root->minmax_aggs = best_path->mmaggregates;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.cindex c729a99f8b..93b8c761c2 100644--- a/src/backend/optimizer/plan/planner.c+++ b/src/backend/optimizer/plan/planner.c@@ -37,6 +37,7 @@
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
+#include "nodes/relation.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
@@ -126,7 +127,7 @@ 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 grouping_planner(PlannerInfo *root, bool inheritance_update,
- double tuple_fraction);+ RelOptInfo *planned_rel, double tuple_fraction);
static grouping_sets_data *preprocess_grouping_sets(PlannerInfo *root);
static List *remap_to_groupclause_idx(List *groupClause, List *gsets,
int *tleref_to_colnum_map);
@@ -630,7 +631,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->grouping_map = NULL;
root->minmax_aggs = NIL;
root->qual_security_level = 0;
- root->inhTargetKind = INHKIND_NONE;
root->hasRecursion = hasRecursion;
if (hasRecursion)
root->wt_param_id = SS_assign_special_param(root);
@@ -970,7 +970,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
rt_fetch(parse->resultRelation, parse->rtable)->inh)
inheritance_planner(root);
else
- grouping_planner(root, false, tuple_fraction);+ grouping_planner(root, false, NULL, tuple_fraction);
/*
* Capture the set of outer-level param IDs we have access to, for use in
@@ -1146,12 +1146,19 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr)
* inheritance set.
*
* We have to handle this case differently from cases where a source relation
- * is an inheritance set. Source inheritance is expanded at the bottom of the- * plan tree (see allpaths.c), but target inheritance has to be expanded at- * the top. The reason is that for UPDATE, each target relation needs a- * different targetlist matching its own column set. Fortunately,- * the UPDATE/DELETE target can never be the nullable side of an outer join,- * so it's OK to generate the plan this way.+ * is an inheritance set. That's mainly because for UPDATE, each target+ * relation needs a different targetlist matching its own column set. So, we+ * must modify the source scan/join path for each target relation such that it+ * produces the desired target list.+ *+ * The source scan/join paths for individual target relations are still+ * created in allpaths.c by first expanding the inheritance in the usual way+ * by set_append_rel_size, followed by join planning for each target relation+ * separately in make_one_rel. Finally, we apply grouping_planner here to each+ * child scan/join path so that it produces the desired targetlist.+ *+ * Fortunately, the UPDATE/DELETE target can never be the nullable side of an+ * outer join, so it's OK to generate the plan this way.
*
* Returns nothing; the useful output is in the Paths we attach to
* the (UPPERREL_FINAL, NULL) upperrel stored in *root.
@@ -1164,14 +1171,8 @@ inheritance_planner(PlannerInfo *root)
{
Query *parse = root->parse;
int top_parentRTindex = parse->resultRelation;
- Bitmapset *subqueryRTindexes;- Bitmapset *modifiableARIindexes;
int nominalRelation = -1;
Index rootRelation = 0;
- List *final_rtable = NIL;- int save_rel_array_size = 0;- RelOptInfo **save_rel_array = NULL;- AppendRelInfo **save_append_rel_array = NULL;
List *subpaths = NIL;
List *subroots = NIL;
List *resultRelations = NIL;
@@ -1179,70 +1180,60 @@ inheritance_planner(PlannerInfo *root)
List *returningLists = NIL;
List *rowMarks;
RelOptInfo *final_rel;
- ListCell *lc;- Index rti;+ ListCell *lc1,+ *lc2;
RangeTblEntry *parent_rte;
- PlannerInfo *parent_root;- Query *parent_parse;- Bitmapset *parent_relids = bms_make_singleton(top_parentRTindex);- PlannerInfo **parent_roots = NULL;+ List *tlist,+ *orig_tlist;+ standard_qp_extra qp_extra;+ RelOptInfo *planned_rel;+ List *partitioned_rels = NIL;+ /* Inheritance is never used for insert. */
Assert(parse->commandType != CMD_INSERT);
+ parent_rte = planner_rt_fetch(top_parentRTindex, root);
/*
- * 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- * same rangetable, but each instance must have its own set of subquery- * RTEs within the finished rangetable because (1) they are likely to get- * scribbled on during planning, and (2) it's not inconceivable that- * subqueries could get planned differently in different cases. We need- * not create duplicate copies of other RTE kinds, in particular not the- * target relations, because they don't have either of those issues. Not- * having to duplicate the target relations is important because doing so- * (1) would result in a rangetable of length O(N^2) for N targets, with- * at least O(N^3) work expended here; and (2) would greatly complicate- * management of the rowMarks list.+ * Generate the access paths for all relations mentioned in the query,+ * including the target inheritance set. When doing the join planning,+ * references in the join tree to the original target relation that's the+ * root parent of the inheritance tree is replaced by each of its+ * inheritance children and the resulting joinrel RelOptInfo's are+ * added to root->inh_target_child_joinrels.
*
- * To begin with, generate a bitmapset of the relids of the subquery RTEs.+ * Final planning steps (grouping_planner) are applied to the best path+ * of each of those child joinrels using a modified instance of the+ * original query for a given child target rel. All the paths so generated+ * are put into a list that will be controlled by a single ModifyTable+ * node. All the instances share the same rangetable.
*/
- subqueryRTindexes = NULL;- rti = 1;- foreach(lc, parse->rtable)- {- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);-- if (rte->rtekind == RTE_SUBQUERY)- subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);- rti++;- }
/*
- * 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.+ * Save the unmodified version of the query's targetlist to be used below+ * for passing to grouping_planner for each child target relation.
*/
- modifiableARIindexes = NULL;- if (subqueryRTindexes != NULL)- {- foreach(lc, root->append_rel_list)- {- AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);+ orig_tlist = list_copy(root->parse->targetList);- 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);- }+ /* Do the scan/join planning. */+ tlist = preprocess_targetlist(root);+ qp_extra.tlist = tlist;+ qp_extra.activeWindows = qp_extra.groupClause = NIL;+ planned_rel = query_planner(root, tlist, standard_qp_callback, &qp_extra);++ /*+ * If it turned out during query planning that all the children are dummy+ * (pruned or excluded by constraints), no need to do the steps below.+ * Let grouping_planner finish up the final path.+ */+ if (IS_DUMMY_REL(planned_rel))+ {+ grouping_planner(root, false, planned_rel, 0.0);+ return;
}
+ Assert(planned_rel->relid == top_parentRTindex);+ Assert(planned_rel->reloptkind == RELOPT_BASEREL);+
/*
* If the parent RTE is a partitioned table, we should use that as the
* nominal target relation, because the RTEs added for partitioned tables
@@ -1250,67 +1241,50 @@ 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;
rootRelation = top_parentRTindex;
+ partitioned_rels = lappend(partitioned_rels,+ list_make1_int(top_parentRTindex));
}
/*
- * The PlannerInfo for each child is obtained by translating the relevant- * members of the PlannerInfo for its immediate parent, which we find- * using the parent_relid in its AppendRelInfo. We save the PlannerInfo- * for each parent in an array indexed by relid for fast retrieval. Since- * the maximum number of parents is limited by the number of RTEs in the- * query, we use that number to allocate the array. An extra entry is- * needed since relids start from 1.+ * Get on with finalizing the path for each child target relation by+ * calling grouping_planner on its joinrel. Note that we're restoring+ * the query's targetlist to the original one for grouping_planner's+ * targetlist expansion steps to perform the expansion with individual+ * child descriptors.
*/
- parent_roots = (PlannerInfo **) palloc0((list_length(parse->rtable) + 1) *- sizeof(PlannerInfo *));- parent_roots[top_parentRTindex] = root;-- /*- * And now we can get on with generating a plan for each child table.- */- foreach(lc, root->append_rel_list)+ parse->targetList = orig_tlist;+ forboth(lc1, root->inh_target_child_rels,+ lc2, root->inh_target_child_joinrels)
{
- AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);+ AppendRelInfo *appinfo = root->append_rel_array[lfirst_int(lc1)];+ RelOptInfo *child_joinrel = lfirst(lc2);
PlannerInfo *subroot;
RangeTblEntry *child_rte;
RelOptInfo *sub_final_rel;
Path *subpath;
-- /* append_rel_list contains all append rels; ignore others */- if (!bms_is_member(appinfo->parent_relid, parent_relids))- continue;-- /*- * expand_inherited_rtentry() always processes a parent before any of- * that parent's children, so the parent_root for this relation should- * already be available.- */- parent_root = parent_roots[appinfo->parent_relid];- Assert(parent_root != NULL);- parent_parse = parent_root->parse;+ Relids child_relids = bms_make_singleton(appinfo->child_relid),+ top_parent_relids = bms_make_singleton(top_parentRTindex);
/*
* We need a working copy of the PlannerInfo so that we can control
* propagation of information back to the main copy.
*/
subroot = makeNode(PlannerInfo);
- memcpy(subroot, parent_root, sizeof(PlannerInfo));+ memcpy(subroot, root, sizeof(PlannerInfo));
/*
* 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,- * then fool around with subquery RTEs.+ * references to the parent RTE to refer to the current child RTE.
*/
subroot->parse = (Query *)
- adjust_appendrel_attrs(parent_root,- (Node *) parent_parse,- 1, &appinfo);+ adjust_appendrel_attrs_multilevel(root, (Node *) parse,+ child_relids,+ top_parent_relids);
/*
* If there are securityQuals attached to the parent, move them to the
@@ -1322,33 +1296,10 @@ inheritance_planner(PlannerInfo *root)
parent_rte->securityQuals = NIL;
/*
- * Mark whether we're planning a query to a partitioned table or an- * inheritance parent.+ * inheritance_make_rel_from_joinlist only adds the leaf partitions to+ * PlannerInfo.
*/
- subroot->inhTargetKind =- (rootRelation != 0) ? INHKIND_PARTITIONED : INHKIND_INHERITED;-- /*- * If this child is further partitioned, remember it as a parent.- * Since a partitioned table does not have any data, we don't need to- * create a plan for it, and we can stop processing it here. We do,- * however, need to remember its modified PlannerInfo for use when- * processing its children, since we'll update their varnos based on- * the delta from immediate parent to child, not from top to child.- *- * Note: a very non-obvious point is that we have not yet added- * duplicate subquery RTEs to the subroot's rtable. We mustn't,- * because then its children would have two sets of duplicates,- * confusing matters.- */- if (child_rte->inh)- {- Assert(child_rte->relkind == RELKIND_PARTITIONED_TABLE);- parent_relids = bms_add_member(parent_relids, appinfo->child_relid);- parent_roots[appinfo->child_relid] = subroot;-- continue;- }+ Assert(!child_rte->inh);
/*
* Set the nominal target relation of the ModifyTable node if not
@@ -1374,111 +1325,8 @@ inheritance_planner(PlannerInfo *root)
if (nominalRelation < 0)
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- * valid-looking.- */- while (list_length(subroot->parse->rtable) < list_length(final_rtable))- subroot->parse->rtable = lappend(subroot->parse->rtable,- makeNode(RangeTblEntry));-- /*- * 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.- */- if (final_rtable != NIL && subqueryRTindexes != NULL)- {- ListCell *lr;-- rti = 1;- foreach(lr, parent_parse->rtable)- {- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lr);-- if (bms_is_member(rti, subqueryRTindexes))- {- Index newrti;-- /*- * The RTE can't contain any references to its own RT- * index, except in its securityQuals, so we can save a- * few cycles by applying ChangeVarNodes to the rest of- * the rangetable before we append the RTE to it.- */- 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);- }- }- rte = copyObject(rte);- ChangeVarNodes((Node *) rte->securityQuals, rti, newrti, 0);- subroot->parse->rtable = lappend(subroot->parse->rtable,- rte);- }- rti++;- }- }-- /* There shouldn't be any OJ info to translate, as yet */- Assert(subroot->join_info_list == NIL);- /* and we haven't created PlaceHolderInfos, either */- Assert(subroot->placeholder_list == NIL);-- /* Generate Path(s) for accessing this result relation */- grouping_planner(subroot, true, 0.0 /* retrieve all tuples */ );+ /* Apply the top-level targetlist to child_joinrel's path. */+ grouping_planner(subroot, true, child_joinrel, 0.0);
/*
* Select cheapest path in case there's more than one. We always run
@@ -1490,45 +1338,10 @@ inheritance_planner(PlannerInfo *root)
subpath = sub_final_rel->cheapest_total_path;
/*
- * If this child rel was excluded by constraint exclusion, exclude it- * from the result plan.+ * child rel cannot be empty, or inheritance_make_rel_from_joinlist+ * wouldn't have put it in the list to begin with.
*/
- if (IS_DUMMY_PATH(subpath))- continue;-- /*- * If this is the first non-excluded child, its post-planning rtable- * 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;- else- final_rtable = list_concat(final_rtable,- list_copy_tail(subroot->parse->rtable,- list_length(final_rtable)));-- /*- * We need to collect all the RelOptInfos from all child plans into- * the main PlannerInfo, since setrefs.c will need them. We use the- * last child's simple_rel_array (previous ones are too short), so we- * 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++)- {- RelOptInfo *brel = 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;- save_append_rel_array = subroot->append_rel_array;-- /* Make sure any initplans from this rel get into the outer list */- root->init_plans = subroot->init_plans;+ Assert(!IS_DUMMY_PATH(subpath));
/* Build list of sub-paths */
subpaths = lappend(subpaths, subpath);
@@ -1560,36 +1373,6 @@ inheritance_planner(PlannerInfo *root)
*/
/*
- * 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;- }-- /*- * 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;- root->append_rel_array = save_append_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 = lfirst_node(RangeTblEntry, 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.
@@ -1629,6 +1412,12 @@ inheritance_planner(PlannerInfo *root)
* (inheritance_planner will create a single ModifyTable node covering all the
* target tables.)
*
+ * If non-NULL, planned_rel is a RelOptInfo containing paths for the query's+ * top-level joinrel, which the caller produced by itself. In that case, this+ * function only needs to adjust the targetlist of its cheapest_total_path.+ * The only caller that may pass such a RelOptInfo currently is+ * inheritance_planner.+ *
* tuple_fraction is the fraction of tuples we expect will be retrieved.
* tuple_fraction is interpreted as follows:
* 0: expect all tuples to be retrieved (normal case)
@@ -1647,6 +1436,7 @@ inheritance_planner(PlannerInfo *root)
*/
static void
grouping_planner(PlannerInfo *root, bool inheritance_update,
+ RelOptInfo *planned_rel,
double tuple_fraction)
{
Query *parse = root->parse;
@@ -1659,7 +1449,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
List *final_targets;
List *final_targets_contain_srfs;
bool final_target_parallel_safe;
- RelOptInfo *current_rel;+ RelOptInfo *current_rel = planned_rel;
RelOptInfo *final_rel;
ListCell *lc;
@@ -1699,6 +1489,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* special work for recursive unions is the responsibility of
* plan_set_operations.
*/
+ Assert(current_rel == NULL);
current_rel = plan_set_operations(root);
/*
@@ -1878,8 +1669,9 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* We also generate (in standard_qp_callback) pathkey representations
* of the query's sort clause, distinct clause, etc.
*/
- current_rel = query_planner(root, tlist,- standard_qp_callback, &qp_extra);+ if (current_rel == NULL)+ current_rel = query_planner(root, tlist,+ standard_qp_callback, &qp_extra);
/*
* Convert the query's result tlist into PathTarget format.
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.cindex cd6e11904e..78baec00dc 100644--- a/src/backend/optimizer/prep/prepjointree.c+++ b/src/backend/optimizer/prep/prepjointree.c@@ -917,7 +917,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->grouping_map = NULL;
subroot->minmax_aggs = NIL;
subroot->qual_security_level = 0;
- subroot->inhTargetKind = INHKIND_NONE;
subroot->hasRecursion = false;
subroot->wt_param_id = -1;
subroot->non_recursive_path = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.cindex d5720518a8..ef1f978889 100644--- a/src/backend/optimizer/prep/prepunion.c+++ b/src/backend/optimizer/prep/prepunion.c@@ -2253,8 +2253,39 @@ adjust_appendrel_attrs_mutator(Node *node,
context->appinfos);
return (Node *) phv;
}
++ /*+ * This is needed, because inheritance_make_rel_from_joinlist needs to+ * translate root->join_info_list executing make_rel_from_joinlist for a+ * given child.+ */+ if (IsA(node, SpecialJoinInfo))+ {+ SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node;+ SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo);++ memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo));+ newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand,+ context->nappinfos,+ context->appinfos);+ newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand,+ context->nappinfos,+ context->appinfos);+ newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand,+ context->nappinfos,+ context->appinfos);+ newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand,+ context->nappinfos,+ context->appinfos);+ newinfo->semi_rhs_exprs =+ (List *) expression_tree_mutator((Node *)+ oldinfo->semi_rhs_exprs,+ adjust_appendrel_attrs_mutator,+ (void *) context);+ return (Node *) newinfo;+ }+
/* Shouldn't need to handle planner auxiliary nodes here */
- Assert(!IsA(node, SpecialJoinInfo));
Assert(!IsA(node, AppendRelInfo));
Assert(!IsA(node, PlaceHolderInfo));
Assert(!IsA(node, MinMaxAggInfo));
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.cindex 0c88c90de4..ed0953f9e1 100644--- a/src/backend/optimizer/util/plancat.c+++ b/src/backend/optimizer/util/plancat.c@@ -1264,36 +1264,6 @@ get_relation_constraints(PlannerInfo *root,
}
}
- /*- * Append partition predicates, if any.- *- * For selects, partition pruning uses the parent table's partition bound- * descriptor, instead of constraint exclusion which is driven by the- * individual partition's partition constraint.- */- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)- {- List *pcqual = RelationGetPartitionQual(relation);-- if (pcqual)- {- /*- * Run the partition quals through const-simplification similar to- * check constraints. We skip canonicalize_qual, though, because- * partition quals should be in canonical form already; also,- * since the qual is in implicit-AND format, we'd have to- * explicitly convert it to explicit-AND format and back again.- */- pcqual = (List *) eval_const_expressions(root, (Node *) pcqual);-- /* Fix Vars to have the desired varno */- if (varno != 1)- ChangeVarNodes((Node *) pcqual, 1, varno, 0);-- result = list_concat(result, pcqual);- }- }-
heap_close(relation, NoLock);
return result;
@@ -1420,31 +1390,15 @@ relation_excluded_by_constraints(PlannerInfo *root,
switch (constraint_exclusion)
{
case CONSTRAINT_EXCLUSION_OFF:
-- /*- * Don't prune if feature turned off -- except if the relation is- * a partition. While partprune.c-style partition pruning is not- * yet in use for all cases (update/delete is not handled), it- * would be a UI horror to use different user-visible controls- * depending on such a volatile implementation detail. Therefore,- * for partitioned tables we use enable_partition_pruning to- * control this behavior.- */- if (root->inhTargetKind == INHKIND_PARTITIONED)- break;
return false;
case CONSTRAINT_EXCLUSION_PARTITION:
/*
* When constraint_exclusion is set to 'partition' we only handle
- * OTHER_MEMBER_RELs, or BASERELs in cases where the result target- * is an inheritance parent or a partitioned table.+ * OTHER_MEMBER_RELs.
*/
- if ((rel->reloptkind != RELOPT_OTHER_MEMBER_REL) &&- !(rel->reloptkind == RELOPT_BASEREL &&- root->inhTargetKind != INHKIND_NONE &&- rel->relid == root->parse->resultRelation))+ if (rel->reloptkind != RELOPT_OTHER_MEMBER_REL)
return false;
break;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.hindex 32ac3315a4..29339d6c72 100644--- a/src/include/nodes/relation.h+++ b/src/include/nodes/relation.h@@ -319,9 +319,6 @@ typedef struct PlannerInfo
Index qual_security_level; /* minimum security_level for quals */
/* Note: qual_security_level is zero if there are no securityQuals */
- InheritanceKind inhTargetKind; /* indicates if the target relation is an- * inheritance child or partition or a- * partitioned table */
bool hasJoinRTEs; /* true if any RTEs are RTE_JOIN kind */
bool hasLateralRTEs; /* true if any RTEs are marked LATERAL */
bool hasDeletedRTEs; /* true if any RTE was deleted from jointree */
@@ -343,6 +340,21 @@ typedef struct PlannerInfo
/* Does this query modify any partition key columns? */
bool partColsUpdated;
++ /*+ * The following fields are set during query planning portion of an+ * inherited UPDATE/DEELETE operation.+ */++ /* RT indexes of child target rels */+ List *inh_target_child_rels;++ /*+ * RelOptInfos representing the output of make_rel_from_joinlist() for+ * a each child rel in the above list, acting as the target relation in+ * place of the original parent target relation.+ */+ List *inh_target_child_joinrels;
} PlannerInfo;
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.outindex 3ba3aaf2d8..a539280851 100644--- a/src/test/regress/expected/partition_join.out+++ b/src/test/regress/expected/partition_join.out@@ -1781,7 +1781,7 @@ WHERE EXISTS (
Filter: (c IS NULL)
-> Nested Loop
-> Seq Scan on int4_tbl
- -> Subquery Scan on ss_1+ -> Subquery Scan on ss
-> Limit
-> Seq Scan on int8_tbl int8_tbl_1
-> Nested Loop Semi Join
@@ -1789,7 +1789,7 @@ WHERE EXISTS (
Filter: (c IS NULL)
-> Nested Loop
-> Seq Scan on int4_tbl
- -> Subquery Scan on ss_2+ -> Subquery Scan on ss
-> Limit
-> Seq Scan on int8_tbl int8_tbl_2
(28 rows)
--
2.11.0
From 42c9c879f1793ea1fb5a6328e853139a40cfcc8d Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 17 Oct 2018 18:14:13 +0900
Subject: [PATCH v5 3/6] Lazy creation of RTEs for inheritance children
Currently, expand_inherited_tables called from subquery_planner opens
all inheritance child tables and adds them to the query's range
table and PlannerInfo in the form of AppendRelInfo's initially and
later RelOptInfo's. For partitioning, that's pretty wasteful, because
it's possible to determine only the partitions that will need to be
scanned using partition pruning, which doesn't require opening the
partitions themselves. However, the point at which the above
processing happens is too early to perform partition pruning.
This commit rearranges things within the planner so that inheritance
child relations can be added to the Query and PlannerInfo when
peforming set_append_rel_size on the parent table. Although the late
initialization approach only benefits the partitioning case, it seems
undesirable to do it only for partitioned tables, because it means
adding code to handle partitions specially at various places within
the planner. So, *all* inheritance parent tables are expanded at
set_append_rel_size. All unpruned partitions are added to Query
in the form of their RangeTblEntry's added to the range table and to
PlannerInfo in the form of an AppendRelInfo and a RelOptInfo for each.
For partitioning, although we don't create a RangeTblEntry and
RelOptInfo for pruned partitions at set_append_rel_size time,
partitionwise join code relies on the fact that even though partitions
may have been pruned, they'd still own a RelOptInfo to handle the
outer join case where the pruned partition appears on the nullable
side of join. Partitionwise join code deals with that by allocating
dummy RelOptInfos for pruned partitions that are based mostly on
their parent's properties.
There are some regression test diffs:
1. Caused by the fact that we no longer allocate a duplicate RT
entry for a partitioned table in its role as child and also that
the individual partition RT entries are now created in the order
in which their parent's are processed, whereas previously they'd
be added to the range table in the order of depth-first expansion
of inheritance tree.
2. Those in postgres_fdw.out are caused by the fact that junk columns
required for row marking are added to reltarget->exprs later than
user columns, because the row marking junk columns arent added
until after the inheritance is expanded which as of this commit
is later than it used to be.
---
contrib/postgres_fdw/expected/postgres_fdw.out | 32 +-
src/backend/optimizer/path/allpaths.c | 204 +-----
src/backend/optimizer/path/joinrels.c | 62 ++
src/backend/optimizer/plan/initsplan.c | 58 --
src/backend/optimizer/plan/planner.c | 54 +-
src/backend/optimizer/plan/setrefs.c | 6 +
src/backend/optimizer/prep/preptlist.c | 11 +-
src/backend/optimizer/prep/prepunion.c | 818 +++++++++++++++-------
src/backend/optimizer/util/Makefile | 2 +-
src/backend/optimizer/util/plancat.c | 41 +-
src/backend/optimizer/util/relnode.c | 80 +--
src/backend/partitioning/partprune.c | 43 +-
src/include/nodes/relation.h | 8 +
src/include/optimizer/pathnode.h | 1 +
src/include/optimizer/plancat.h | 2 +-
src/include/optimizer/prep.h | 15 +-
src/include/partitioning/partprune.h | 2 +-
src/test/regress/expected/join.out | 22 +-
src/test/regress/expected/partition_aggregate.out | 4 +-
19 files changed, 834 insertions(+), 631 deletions(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.outindex 21a2ef5ad3..c930bf7bab 100644--- a/contrib/postgres_fdw/expected/postgres_fdw.out+++ b/contrib/postgres_fdw/expected/postgres_fdw.out@@ -7085,15 +7085,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.f1, foo.ctid, foo.*, foo.tableoid
-> HashAggregate
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.*, foo.tableoid
Group Key: foo.f1
-> Append
-> Seq Scan on public.foo
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.*, foo.tableoid
-> Foreign Scan on public.foo2
- Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1+ Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
(23 rows)
@@ -7123,15 +7123,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.f1, foo.ctid, foo.*, foo.tableoid
-> HashAggregate
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.*, foo.tableoid
Group Key: foo.f1
-> Append
-> Seq Scan on public.foo
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.*, foo.tableoid
-> Foreign Scan on public.foo2
- Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1+ Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
(23 rows)
@@ -7160,15 +7160,15 @@ 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.f1, foo.ctid, foo.tableoid, foo.*
-> HashAggregate
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.tableoid, foo.*
Group Key: foo.f1
-> Append
-> Seq Scan on public.foo
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.tableoid, foo.*
-> Foreign Scan on public.foo2
- Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1+ Output: foo2.f1, foo2.ctid, foo2.tableoid, foo2.*
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
@@ -7178,15 +7178,15 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
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.f1, foo.ctid, foo.tableoid, foo.*
-> HashAggregate
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.tableoid, foo.*
Group Key: foo.f1
-> Append
-> Seq Scan on public.foo
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.tableoid, foo.*
-> Foreign Scan on public.foo2
- Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1+ Output: foo2.f1, foo2.ctid, foo2.tableoid, foo2.*
Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
(39 rows)
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.cindex 8de8d383f9..6ee800d84a 100644--- a/src/backend/optimizer/path/allpaths.c+++ b/src/backend/optimizer/path/allpaths.c@@ -44,7 +44,6 @@
#include "optimizer/var.h"
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
-#include "partitioning/partprune.h"
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
@@ -943,8 +942,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
double *parent_attrsizes;
int nattrs;
ListCell *l;
- Relids live_children = NULL;- bool did_pruning = false;
/* Guard against stack overflow due to overly deep inheritance tree. */
check_stack_depth();
@@ -952,29 +949,22 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
Assert(IS_SIMPLE_REL(rel));
/*
- * Initialize partitioned_child_rels to contain this RT index.- *- * Note that during the set_append_rel_pathlist() phase, we will bubble up- * the indexes of partitioned relations that appear down in the tree, so- * that when we've created Paths for all the children, the root- * partitioned table's list will contain all such indexes.+ * Add append children to the PlannerInfo.
*/
- if (rte->relkind == RELKIND_PARTITIONED_TABLE)- rel->partitioned_child_rels = list_make1_int(rti);+ expand_append_rel(root, rel, rte, rti);++ /* Did we find out that none of the children produce any output? */+ if (IS_DUMMY_REL(rel))+ return;
/*
- * If the partitioned relation has any baserestrictinfo quals then we- * attempt to use these quals to prune away partitions that cannot- * possibly contain any tuples matching these quals. In this case we'll- * store the relids of all partitions which could possibly contain a- * matching tuple, and skip anything else in the loop below.+ * If we only need to scan the parent because all of the children are+ * either pruned or excluded, do so right away and return.
*/
- if (enable_partition_pruning &&- rte->relkind == RELKIND_PARTITIONED_TABLE &&- rel->baserestrictinfo != NIL)+ if (!rte->inh)
{
- live_children = prune_append_rel_partitions(rel);- did_pruning = true;+ set_rel_size(root, rel, rti, rte);+ return;
}
/*
@@ -1014,12 +1004,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
int childRTindex;
RangeTblEntry *childRTE;
RelOptInfo *childrel;
- List *childquals;- Index cq_min_security;- bool have_const_false_cq;
ListCell *parentvars;
ListCell *childvars;
- ListCell *lc;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
@@ -1027,18 +1013,31 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
childRTindex = appinfo->child_relid;
childRTE = root->simple_rte_array[childRTindex];
+ Assert(childRTE != NULL);- /*- * The child rel's RelOptInfo was already created during- * add_base_rels_to_query.- */+ /* The child rel's RelOptInfo is created during expand_append_rel */
childrel = find_base_rel(root, childRTindex);
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
/*
- * Copy/Modify targetlist. Even if this child is deemed empty, we need- * its targetlist in case it falls on nullable side in a child-join- * because of partitionwise join.+ * Child relation may have be marked dummy if build_append_child_rel+ * found self-contradictory quals.+ */+ if (IS_DUMMY_REL(childrel))+ continue;++ if (relation_excluded_by_constraints(root, childrel, childRTE))+ {+ /*+ * This child need not be scanned, so we can omit it from the+ * appendrel.+ */+ set_dummy_rel_pathlist(childrel);+ continue;+ }++ /*+ * Copy/Modify targetlist.
*
* NB: the resulting childrel->reltarget->exprs may contain arbitrary
* expressions, which otherwise would not occur in a rel's targetlist.
@@ -1067,144 +1066,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
add_child_rel_equivalences(root, appinfo, rel, childrel);
childrel->has_eclass_joins = rel->has_eclass_joins;
- /*- * We have to copy the parent's quals to the child, with appropriate- * substitution of variables. However, only the baserestrictinfo- * quals are needed before we can check for constraint exclusion; so- * do that first and then check to see if we can disregard this child.- *- * The child rel's targetlist might contain non-Var expressions, which- * means that substitution into the quals could produce opportunities- * for const-simplification, and perhaps even pseudoconstant quals.- * Therefore, transform each RestrictInfo separately to see if it- * reduces to a constant or pseudoconstant. (We must process them- * separately to keep track of the security level of each qual.)- */- childquals = NIL;- cq_min_security = UINT_MAX;- have_const_false_cq = false;- foreach(lc, rel->baserestrictinfo)- {- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);- Node *childqual;- ListCell *lc2;-- Assert(IsA(rinfo, RestrictInfo));- childqual = adjust_appendrel_attrs(root,- (Node *) rinfo->clause,- 1, &appinfo);- childqual = eval_const_expressions(root, childqual);- /* check for flat-out constant */- if (childqual && IsA(childqual, Const))- {- if (((Const *) childqual)->constisnull ||- !DatumGetBool(((Const *) childqual)->constvalue))- {- /* Restriction reduces to constant FALSE or NULL */- have_const_false_cq = true;- break;- }- /* Restriction reduces to constant TRUE, so drop it */- continue;- }- /* might have gotten an AND clause, if so flatten it */- foreach(lc2, make_ands_implicit((Expr *) childqual))- {- Node *onecq = (Node *) lfirst(lc2);- bool pseudoconstant;-- /* check for pseudoconstant (no Vars or volatile functions) */- pseudoconstant =- !contain_vars_of_level(onecq, 0) &&- !contain_volatile_functions(onecq);- if (pseudoconstant)- {- /* tell createplan.c to check for gating quals */- root->hasPseudoConstantQuals = true;- }- /* reconstitute RestrictInfo with appropriate properties */- childquals = lappend(childquals,- make_restrictinfo((Expr *) onecq,- rinfo->is_pushed_down,- rinfo->outerjoin_delayed,- pseudoconstant,- rinfo->security_level,- NULL, NULL, NULL));- /* track minimum security level among child quals */- cq_min_security = Min(cq_min_security, rinfo->security_level);- }- }-- /*- * In addition to the quals inherited from the parent, we might have- * securityQuals associated with this particular child node.- * (Currently this can only happen in appendrels originating from- * UNION ALL; inheritance child tables don't have their own- * securityQuals, see expand_inherited_rtentry().) Pull any such- * securityQuals up into the baserestrictinfo for the child. This is- * similar to process_security_barrier_quals() for the parent rel,- * except that we can't make any general deductions from such quals,- * since they don't hold for the whole appendrel.- */- if (childRTE->securityQuals)- {- Index security_level = 0;-- foreach(lc, childRTE->securityQuals)- {- List *qualset = (List *) lfirst(lc);- ListCell *lc2;-- foreach(lc2, qualset)- {- Expr *qual = (Expr *) lfirst(lc2);-- /* not likely that we'd see constants here, so no check */- childquals = lappend(childquals,- make_restrictinfo(qual,- true, false, false,- security_level,- NULL, NULL, NULL));- cq_min_security = Min(cq_min_security, security_level);- }- security_level++;- }- Assert(security_level <= root->qual_security_level);- }-- /*- * OK, we've got all the baserestrictinfo quals for this child.- */- childrel->baserestrictinfo = childquals;- childrel->baserestrict_min_security = cq_min_security;-- if (have_const_false_cq)- {- /*- * Some restriction clause reduced to constant FALSE or NULL after- * substitution, so this child need not be scanned.- */- set_dummy_rel_pathlist(childrel);- continue;- }-- if (did_pruning && !bms_is_member(appinfo->child_relid, live_children))- {- /* This partition was pruned; skip it. */- set_dummy_rel_pathlist(childrel);- continue;- }-- if (relation_excluded_by_constraints(root, childrel, childRTE))- {- /*- * This child need not be scanned, so we can omit it from the- * appendrel.- */- set_dummy_rel_pathlist(childrel);- continue;- }-
/* CE failed, so finish copying/modifying join quals. */
childrel->joininfo = (List *)
adjust_appendrel_attrs(root,
@@ -1275,7 +1136,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
child_relids,
top_parent_relids);
root->parse = parse;
- tlist = preprocess_targetlist(root);+ tlist = preprocess_targetlist(root, true);
build_base_rel_tlists(root, tlist);
root->parse = orig_parse;
@@ -2744,6 +2605,7 @@ inheritance_make_rel_from_joinlist(PlannerInfo *root,
continue;
childrel = find_base_rel(root, appinfo->child_relid);
+ Assert(childrel != NULL);
/* Ignore excluded/pruned children. */
if (IS_DUMMY_REL(childrel))
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.cindex d3d21fed5d..da0831de4e 100644--- a/src/backend/optimizer/path/joinrels.c+++ b/src/backend/optimizer/path/joinrels.c@@ -20,6 +20,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/prep.h"
+#include "optimizer/tlist.h"
#include "partitioning/partbounds.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -46,6 +47,9 @@ static void try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1,
List *parent_restrictlist);
static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel,
bool strict_op);
+static RelOptInfo *build_dummy_partition_rel(PlannerInfo *root,+ RelOptInfo *parent,+ int partidx);
/*
@@ -1376,6 +1380,11 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
AppendRelInfo **appinfos;
int nappinfos;
+ if (child_rel1 == NULL)+ child_rel1 = build_dummy_partition_rel(root, rel1, cnt_parts);+ if (child_rel2 == NULL)+ child_rel2 = build_dummy_partition_rel(root, rel2, cnt_parts);+
/* We should never try to join two overlapping sets of rels. */
Assert(!bms_overlap(child_rel1->relids, child_rel2->relids));
child_joinrelids = bms_union(child_rel1->relids, child_rel2->relids);
@@ -1582,3 +1591,56 @@ match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op)
return -1;
}
++/*+ * build_dummy_partition_rel+ * Build a RelOptInfo and AppendRelInfo for a pruned partition+ *+ * This does not result in opening the relation or a range table entry being+ * created. Also, the RelOptInfo thus created is not stored anywhere else+ * beside the parent's part_rels array.+ *+ * The only reason this exists is because partition-wise join, in some cases,+ * needs a RelOptInfo to represent an empty relation that's on the nullable+ * side of an outer join, so that a Path representing the outer join can be+ * created.+ */+RelOptInfo *+build_dummy_partition_rel(PlannerInfo *root, RelOptInfo *parent, int partidx)+{+ RangeTblEntry *parentrte = root->simple_rte_array[parent->relid];+ RelOptInfo *rel;++ Assert(parent->part_rels[partidx] == NULL);++ /* Create minimally valid-looking RelOptInfo with parent's relid. */+ rel = makeNode(RelOptInfo);+ rel->reloptkind = RELOPT_OTHER_MEMBER_REL;+ rel->relid = parent->relid;+ rel->relids = bms_copy(parent->relids);+ if (parent->top_parent_relids)+ rel->top_parent_relids = parent->top_parent_relids;+ else+ rel->top_parent_relids = bms_copy(parent->relids);+ rel->reltarget = copy_pathtarget(parent->reltarget);+ parent->part_rels[partidx] = rel;+ mark_dummy_rel(rel);++ /*+ * Now we'll need a (no-op) AppendRelInfo for parent, because we're+ * setting the dummy partition's relid to be same as the parent's.+ */+ if (root->append_rel_array[parent->relid] == NULL)+ {+ /* Pass parent's info as for both the parent rel and child rel. */+ AppendRelInfo *appinfo = make_append_rel_info(parent, parentrte,+ parent->tupdesc,+ parentrte->relid,+ parent->reltype,+ parent->relid);++ root->append_rel_array[parent->relid] = appinfo;+ }++ return rel;+}diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.cindex 01335db511..7636aa82c4 100644--- a/src/backend/optimizer/plan/initsplan.c+++ b/src/backend/optimizer/plan/initsplan.c@@ -616,64 +616,6 @@ create_lateral_join_info(PlannerInfo *root)
bms_add_member(brel2->lateral_referencers, rti);
}
}
-- /*- * Lastly, propagate lateral_relids and lateral_referencers from appendrel- * parent rels to their child rels. We intentionally give each child rel- * the same minimum parameterization, even though it's quite possible that- * some don't reference all the lateral rels. This is because any append- * path for the parent will have to have the same parameterization for- * every child anyway, and there's no value in forcing extra- * reparameterize_path() calls. Similarly, a lateral reference to the- * parent prevents use of otherwise-movable join rels for each child.- */- for (rti = 1; rti < root->simple_rel_array_size; rti++)- {- RelOptInfo *brel = root->simple_rel_array[rti];- RangeTblEntry *brte = root->simple_rte_array[rti];-- /*- * Skip empty slots. Also skip non-simple relations i.e. dead- * relations.- */- if (brel == NULL || !IS_SIMPLE_REL(brel))- continue;-- /*- * In the case of table inheritance, the parent RTE is directly linked- * to every child table via an AppendRelInfo. In the case of table- * partitioning, the inheritance hierarchy is expanded one level at a- * time rather than flattened. Therefore, an other member rel that is- * a partitioned table may have children of its own, and must- * therefore be marked with the appropriate lateral info so that those- * children eventually get marked also.- */- Assert(brte);- if (brel->reloptkind == RELOPT_OTHER_MEMBER_REL &&- (brte->rtekind != RTE_RELATION ||- brte->relkind != RELKIND_PARTITIONED_TABLE))- continue;-- if (brte->inh)- {- foreach(lc, root->append_rel_list)- {- AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);- RelOptInfo *childrel;-- if (appinfo->parent_relid != rti)- continue;- childrel = root->simple_rel_array[appinfo->child_relid];- Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);- Assert(childrel->direct_lateral_relids == NULL);- childrel->direct_lateral_relids = brel->direct_lateral_relids;- Assert(childrel->lateral_relids == NULL);- childrel->lateral_relids = brel->lateral_relids;- Assert(childrel->lateral_referencers == NULL);- childrel->lateral_referencers = brel->lateral_referencers;- }- }- }
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.cindex 93b8c761c2..84746a9bc9 100644--- a/src/backend/optimizer/plan/planner.c+++ b/src/backend/optimizer/plan/planner.c@@ -23,6 +23,7 @@
#include "access/sysattr.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"
@@ -699,27 +700,24 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
}
if (rte->lateral)
root->hasLateralRTEs = true;
++ /*+ * While at it, also update the inh status. If the relation doesn't+ * or can't have any children, there is no point in letting inh be set+ * to true. Note that we do this before processing rowmarks, so that+ * the correct information for setting isParent field of PlanRowMarks.+ */+ if (rte->rtekind == RTE_RELATION)+ rte->inh = rte->inh && has_subclass(rte->relid);
}
/*
* 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 base relations not joins, so it's OK (and marginally more- * efficient) to do it after checking for join RTEs. We must do it 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.
@@ -1215,7 +1213,7 @@ inheritance_planner(PlannerInfo *root)
orig_tlist = list_copy(root->parse->targetList);
/* Do the scan/join planning. */
- tlist = preprocess_targetlist(root);+ tlist = preprocess_targetlist(root, false);
qp_extra.tlist = tlist;
qp_extra.activeWindows = qp_extra.groupClause = NIL;
planned_rel = query_planner(root, tlist, standard_qp_callback, &qp_extra);
@@ -1223,9 +1221,10 @@ inheritance_planner(PlannerInfo *root)
/*
* If it turned out during query planning that all the children are dummy
* (pruned or excluded by constraints), no need to do the steps below.
- * Let grouping_planner finish up the final path.+ * For regular inheritance case, it's possible that we only need to modify+ * the parent table. Let grouping_planner finish up the final path.
*/
- if (IS_DUMMY_REL(planned_rel))+ if (!parent_rte->inh || IS_DUMMY_REL(planned_rel))
{
grouping_planner(root, false, planned_rel, 0.0);
return;
@@ -1580,7 +1579,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
}
/* Preprocess targetlist */
- tlist = preprocess_targetlist(root);+ tlist = preprocess_targetlist(root, inheritance_update);
/*
* We are now done hacking up the query's targetlist. Most of the
@@ -1674,6 +1673,15 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
standard_qp_callback, &qp_extra);
/*
+ * Query planning may have added some columns to the top-level tlist,+ * which happens when there are row marks applied to inheritance+ * parent relations (additional junk columns needed for applying row+ * marks are added after expanding inheritance.)+ */+ if (list_length(tlist) < list_length(root->processed_tlist))+ tlist = root->processed_tlist;++ /*
* Convert the query's result tlist into PathTarget format.
*
* Note: it's desirable to not do this till after query_planner(),
@@ -2360,7 +2368,7 @@ preprocess_rowmarks(PlannerInfo *root)
newrc->allMarkTypes = (1 << newrc->markType);
newrc->strength = rc->strength;
newrc->waitPolicy = rc->waitPolicy;
- newrc->isParent = false;+ newrc->isParent = rte->inh;
prowmarks = lappend(prowmarks, newrc);
}
@@ -2385,7 +2393,7 @@ preprocess_rowmarks(PlannerInfo *root)
newrc->allMarkTypes = (1 << newrc->markType);
newrc->strength = LCS_NONE;
newrc->waitPolicy = LockWaitBlock; /* doesn't matter */
- newrc->isParent = false;+ newrc->isParent = rte->rtekind == RTE_RELATION ? rte->inh : false;
prowmarks = lappend(prowmarks, newrc);
}
@@ -6765,6 +6773,10 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
int nappinfos;
List *child_scanjoin_targets = NIL;
+ /* Skip processing pruned partitions. */+ if (child_rel == NULL)+ continue;+
/* Translate scan/join targets for this child. */
appinfos = find_appinfos_by_relids(root, child_rel->relids,
&nappinfos);
@@ -6866,6 +6878,10 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
RelOptInfo *child_grouped_rel;
RelOptInfo *child_partially_grouped_rel;
+ /* Skip processing pruned partitions. */+ if (child_input_rel == NULL)+ continue;+
/* Input child rel must have a path */
Assert(child_input_rel->pathlist != NIL);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.cindex 6d6ef1c376..454870609a 100644--- a/src/backend/optimizer/plan/setrefs.c+++ b/src/backend/optimizer/plan/setrefs.c@@ -326,6 +326,12 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
UPPERREL_FINAL, NULL)))
add_rtes_to_flat_rtable(rel->subroot, true);
}
+ /*+ * A NULL rel also means an unplanned subquery rte, so apply+ * flatten_unplanned_rtes.+ */+ else+ flatten_unplanned_rtes(glob, rte);
}
rti++;
}
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.cindex 8603feef2b..9c0a1c1b1c 100644--- a/src/backend/optimizer/prep/preptlist.c+++ b/src/backend/optimizer/prep/preptlist.c@@ -67,7 +67,7 @@ static List *expand_targetlist(List *tlist, int command_type,
* is also preprocessed (and updated in-place).
*/
List *
-preprocess_targetlist(PlannerInfo *root)+preprocess_targetlist(PlannerInfo *root, bool inheritance_update)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
@@ -134,6 +134,15 @@ preprocess_targetlist(PlannerInfo *root)
if (rc->rti != rc->prti)
continue;
+ /*+ * For inheritance parent row marks, we defer adding junk columns+ * until we've added child row marks, because some children might+ * require different row mark types which affects which junk columns+ * are needed in the top-level tlist.+ */+ if (rc->isParent && !inheritance_update)+ continue;+
if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
{
/* Need to fetch TID */
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.cindex ef1f978889..c6817757e7 100644--- a/src/backend/optimizer/prep/prepunion.c+++ b/src/backend/optimizer/prep/prepunion.c@@ -39,15 +39,19 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
+#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
+#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parsetree.h"
+#include "partitioning/partprune.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
@@ -100,22 +104,22 @@ static List *generate_append_tlist(List *colTypes, List *colCollations,
List *refnames_tlist);
static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
- Index rti);+ Index rti, RelOptInfo *rel);
static void expand_partitioned_rtentry(PlannerInfo *root,
RangeTblEntry *parentrte,
- Index parentRTindex, Relation parentrel,- PlanRowMark *top_parentrc, LOCKMODE lockmode,- List **appinfos);-static void expand_single_inheritance_child(PlannerInfo *root,- RangeTblEntry *parentrte,- Index parentRTindex, Relation parentrel,- PlanRowMark *top_parentrc, Relation childrel,- List **appinfos, RangeTblEntry **childrte_p,- Index *childRTindex_p);-static void make_inh_translation_list(Relation oldrelation,- Relation newrelation,- Index newvarno,- List **translated_vars);+ Index parentRTindex, RelOptInfo *parentrel);+static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root,+ RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel,+ PlanRowMark *top_parentrc, Relation childrel);+static void make_inh_translation_list(TupleDesc old_tupdesc,+ TupleDesc new_tupdesc,+ Oid from_rel, Oid to_rel,+ Index newvarno, List **translated_vars);+static RelOptInfo *build_append_child_rel(PlannerInfo *root,+ RelOptInfo *parent,+ Index childRTindex);+static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc);
static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
List *translated_vars);
static Node *adjust_appendrel_attrs_mutator(Node *node,
@@ -1463,42 +1467,107 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
/*
- * 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.+ * expand_append_rel+ * This initializes RelOptInfos for an appendrel's child relations.+ *+ * 'rel' is the appendrel parent, whose range table entry ('rte') has been+ * marked to require adding children. An appendrel parent could either+ * be a subquery (if we flattened UNION ALL query) or a table that's known+ * to have inheritance children. The latter consists of both regular+ * inheritance parents and partitioned tables.+ *+ * For a subquery parent, there is not much to be done here because the+ * children's RTEs are already present in the query, so we just initialize+ * RelOptInfos for them. Also, the AppendRelInfos for child subqueries+ * have already been added.+ *+ * For tables, we need to add the children to the range table and initialize+ * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them. For+ * a partitioned parent, we only add the children remaining after pruning.+ * For regular inheritance parents, we find the children using+ * find_all_inheritors and add all of them.+ *+ * If it turns out that there are no children, then we set rte->inh to false+ * to let the caller know that only the parent table needs to be scanned. The+ * caller can accordingly switch to a non-Append path. For a partitioned+ * parent, that means an empty relation because parents themselves contain no+ * data.+ *+ * For the regular inheritance case, the parent also gets another RTE with+ * inh = false to represent it as an appendrel child. The original RTE is+ * considered to represent the whole inheritance set.
*/
void
-expand_inherited_tables(PlannerInfo *root)+expand_append_rel(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,+ Index rti)
{
- Index nrtes;- Index rti;- ListCell *rl;+ Assert(rte->inh);+ /* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */+ Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY);
/*
- * 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.+ * UNION ALL children already got RTEs and AppendRelInfos, so just build+ * RelOptInfos and return.+ *+ * It might be a bit odd that this code is in this, because there is+ * nothing to expand really.
*/
- nrtes = list_length(root->parse->rtable);- rl = list_head(root->parse->rtable);- for (rti = 1; rti <= nrtes; rti++)+ if (rte->rtekind == RTE_SUBQUERY)
{
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);+ ListCell *l;- expand_inherited_rtentry(root, rte, rti);- rl = lnext(rl);+ /*+ * We don't need to use expand_planner_arrays in this case, because+ * no new child RTEs are created. setup_simple_rel_arrays() and+ * setup_append_rel_array would've considered these child RTEs when+ * allocating space for various arrays.+ */+ foreach(l, root->append_rel_list)+ {+ AppendRelInfo *appinfo = lfirst(l);+ Index childRTindex = appinfo->child_relid;++ if (appinfo->parent_relid != rti)+ continue;++ Assert(childRTindex < root->simple_rel_array_size);+ Assert(root->simple_rte_array[childRTindex] != NULL);++ /*+ * We set the correct value of baserestricinfo and+ * baserestrict_min_security below.+ */+ root->simple_rel_array[childRTindex] =+ build_append_child_rel(root, rel, appinfo->child_relid);+ }+ }+ else+ {+ Assert(rte->rtekind == RTE_RELATION);+ Assert(has_subclass(rte->relid));++ /*+ * The rewriter should already have obtained an appropriate lock on+ * each relation named in the query. However, for each child relation+ * we add to the query, we must obtain an appropriate lock, because+ * this will be the first use of those relations in the+ * parse/rewrite/plan pipeline. Child rels should use the same+ * lockmode as their parent.+ */+ Assert(rte->rellockmode != NoLock);++ if (rte->relkind == RELKIND_PARTITIONED_TABLE)+ expand_partitioned_rtentry(root, rte, rti, rel);+ else+ expand_inherited_rtentry(root, rte, rti, rel);
}
}
/*
* 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- * rangetable, and build AppendRelInfo nodes for all the child tables- * and add them to root->append_rel_list. If not, clear the entry's- * "inh" flag to prevent later code from looking for AppendRelInfos.+ * Add entries for all the child tables to the query's rangetable, and+ * build AppendRelInfo nodes for all the child tables and add them to+ * root->append_rel_list.
*
* Note that the original RTE is considered to represent the whole
* inheritance set. The first of the generated RTEs is an RTE for the same
@@ -1509,45 +1578,22 @@ expand_inherited_tables(PlannerInfo *root)
* regular inheritance, a parent RTE must always have at least two associated
* AppendRelInfos: one corresponding to the parent table as a simple member of
* inheritance set and one or more corresponding to the actual children.
- * Since a partitioned table is not scanned, it might have only one associated- * AppendRelInfo.
*/
static void
-expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)+expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti,+ RelOptInfo *rel)
{
Oid parentOID;
PlanRowMark *oldrc;
- Relation oldrelation;- LOCKMODE lockmode;+ LOCKMODE lockmode = rte->rellockmode;
List *inhOIDs;
ListCell *l;
+ int num_children;+ int num_children_added = 0;- /* Does RT entry allow inheritance? */- if (!rte->inh)- return;- /* Ignore any already-expanded UNION ALL nodes */- if (rte->rtekind != RTE_RELATION)- {- Assert(rte->rtekind == RTE_SUBQUERY);- return;- }- /* Fast path for common case of childless table */+ Assert(rte->rtekind == RTE_RELATION);+ Assert(lockmode != NoLock);
parentOID = rte->relid;
- if (!has_subclass(parentOID))- {- /* Clear flag before returning */- rte->inh = false;- return;- }-- /*- * The rewriter should already have obtained an appropriate lock on each- * relation named in the query. However, for each child relation we add- * to the query, we must obtain an appropriate lock, because this will be- * the first use of those relations in the parse/rewrite/plan pipeline.- * Child rels should use the same lockmode as their parent.- */- lockmode = rte->rellockmode;
/* Scan for all members of inheritance set, acquire needed locks */
inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
@@ -1557,7 +1603,8 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
* case. This could happen despite above has_subclass() check, if table
* once had a child but no longer does.
*/
- if (list_length(inhOIDs) < 2)+ num_children = list_length(inhOIDs);+ if (num_children < 2)
{
/* Clear flag before returning */
rte->inh = false;
@@ -1565,277 +1612,246 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
}
/*
- * If parent relation is selected FOR UPDATE/SHARE, we need to mark its- * PlanRowMark as isParent = true, and generate a new PlanRowMark for each- * child.+ * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks+ * should've set isParent = true. We'll generate a new PlanRowMark for+ * each child.
*/
oldrc = get_plan_rowmark(root->rowMarks, rti);
- if (oldrc)- oldrc->isParent = true;+ Assert(oldrc == NULL || oldrc->isParent);
/*
- * Must open the parent relation to examine its tupdesc. We need not lock- * it; we assume the rewriter already did.+ * Must expand PlannerInfo arrays by num_children before we can add+ * children.
*/
- oldrelation = heap_open(parentOID, NoLock);+ expand_planner_arrays(root, num_children);- /* Scan the inheritance set and expand it */- if (RelationGetPartitionDesc(oldrelation) != NULL)+ foreach(l, inhOIDs)
{
- Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);+ Oid childOID = lfirst_oid(l);+ Relation newrelation;+ RelOptInfo *childrel;++ /* Already locked above. */+ newrelation = heap_open(childOID, NoLock);
/*
- * If this table has partitions, recursively expand them in the order- * in which they appear in the PartitionDesc. While at it, also- * extract the partition key columns of all the partitioned tables.+ * It is possible that the parent table has children that are temp+ * tables of other backends. We cannot safely access such tables+ * (because of buffering issues), and the best thing to do seems+ * to be to silently ignore them.
*/
- expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,- lockmode, &root->append_rel_list);- }- else- {- List *appinfos = NIL;- RangeTblEntry *childrte;- Index childRTindex;-- /*- * This table has no partitions. Expand any plain inheritance- * children in the order the OIDs were returned by- * find_all_inheritors.- */- foreach(l, inhOIDs)+ if (RELATION_IS_OTHER_TEMP(newrelation))
{
- Oid childOID = lfirst_oid(l);- Relation newrelation;-- /* Open rel if needed; we already have required locks */- if (childOID != parentOID)- newrelation = heap_open(childOID, NoLock);- else- newrelation = oldrelation;-- /*- * It is possible that the parent table has children that are temp- * tables of other backends. We cannot safely access such tables- * (because of buffering issues), and the best thing to do seems- * to be to silently ignore them.- */- if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))- {- heap_close(newrelation, lockmode);- continue;- }-- expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,- newrelation,- &appinfos, &childrte,- &childRTindex);-- /* Close child relations, but keep locks */- if (childOID != parentOID)- heap_close(newrelation, NoLock);+ heap_close(newrelation, lockmode);+ continue;
}
- /*- * If all the children were temp tables, pretend it's a- * non-inheritance situation; we don't need Append node in that case.- * The duplicate RTE we added for the parent table is harmless, so we- * don't bother to get rid of it; ditto for the useless PlanRowMark- * node.- */- if (list_length(appinfos) < 2)- rte->inh = false;- else- root->append_rel_list = list_concat(root->append_rel_list,- appinfos);+ childrel = add_inheritance_child_rel(root, rte, rti, rel, oldrc,+ newrelation);+ Assert(childrel != NULL);+ /* Close child relations, but keep locks */+ heap_close(newrelation, NoLock);+ num_children_added++;
}
- heap_close(oldrelation, NoLock);+ /*+ * If all children, including the parent (as child rel), were+ * excluded, mark the parent rel as empty. If all the children were temp+ * tables, pretend it's a non-inheritance situation; we don't need Append+ * node in that case. The duplicate RTE we added for the parent table is+ * harmless, so we don't bother to get rid of it; ditto for the useless+ * PlanRowMark node.+ */+ if (num_children_added == 0)+ mark_dummy_rel(rel);+ else if (num_children_added == 1)+ rte->inh = false;++ /*+ * Add junk columns needed by the row mark if any and also add the+ * relevant expressions to the root parent's reltarget.+ */+ if (oldrc)+ {+ List *tlist = add_rowmark_junk_columns(root, oldrc);++ build_base_rel_tlists(root, tlist);+ }
}
/*
* expand_partitioned_rtentry
- * Recursively expand an RTE for a partitioned table.+ * Prunes unnecessary partitions of a partitioned table and adds+ * remaining ones to the Query and the PlannerInfo
*
- * Note that RelationGetPartitionDispatchInfo will expand partitions in the- * same order as this code.+ * Partitions are added to the query in order in which they are found in+ * the parent's PartitionDesc.+ *+ * Note: even though only the unpruned partitions will be added to the+ * resulting plan, this still locks *all* partitions via find_all_inheritors+ * in order to avoid partitions being locked in a different order than other+ * places in the backend that may lock partitions.
*/
static void
expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
- Index parentRTindex, Relation parentrel,- PlanRowMark *top_parentrc, LOCKMODE lockmode,- List **appinfos)+ Index parentRTindex, RelOptInfo *parentrel)
{
+ LOCKMODE lockmode = parentrte->rellockmode;+ PlanRowMark *rootrc = NULL;
int i;
- RangeTblEntry *childrte;- Index childRTindex;- PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);+ Bitmapset *partindexes;+ Index rootParentRTindex = parentrel->inh_root_parent > 0 ?+ parentrel->inh_root_parent :+ parentRTindex;- check_stack_depth();-- /* A partitioned table should always have a partition descriptor. */- Assert(partdesc);-- Assert(parentrte->inh);+ /* If root partitioned table, lock *all* partitions in the tree. */+ if (parentRTindex == rootParentRTindex)+ (void) find_all_inheritors(parentrte->relid, lockmode, NULL);
/*
- * 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- * because parentrte already has the root partrel's updatedCols translated- * to match the attribute ordering of parentrel.+ * Initialize partitioned_child_rels to contain this RT index.+ *+ * Note that during the set_append_rel_pathlist() phase, values of the+ * indexes of partitioned relations that appear down in the tree will be+ * bubbled up into root parent's list so that when we've created Paths for+ * all the children, the root table's list will contain all such indexes.
*/
- if (!root->partColsUpdated)- root->partColsUpdated =- has_partition_attrs(parentrel, parentrte->updatedCols, NULL);+ parentrel->partitioned_child_rels = list_make1_int(parentRTindex);- /* First expand the partitioned table itself. */- expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,- top_parentrc, parentrel,- appinfos, &childrte, &childRTindex);+ /* Perform pruning. */+ partindexes = prune_append_rel_partitions(parentrel);++ /* Must expand PlannerInfo arrays before we can add children. */+ expand_planner_arrays(root, bms_num_members(partindexes));
/*
- * If the partitioned table has no partitions, treat this as the- * non-inheritance case.+ * For partitioned tables, we also store the partition RelOptInfo+ * pointers in the parent's RelOptInfo.
*/
- if (partdesc->nparts == 0)- {- parentrte->inh = false;- return;- }+ parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *+ parentrel->nparts);- for (i = 0; i < partdesc->nparts; i++)+ rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex);+ Assert(rootrc == NULL || rootrc->isParent);+ i = -1;+ while ((i = bms_next_member(partindexes, i)) >= 0)
{
- Oid childOID = partdesc->oids[i];- Relation childrel;+ Oid childOID = parentrel->part_oids[i];+ Relation newrelation;+ RelOptInfo *childrel;- /* Open rel; we already have required locks */- childrel = heap_open(childOID, NoLock);+ /* Already locked above. */+ newrelation = heap_open(childOID, NoLock);+ Assert(!RELATION_IS_OTHER_TEMP(newrelation));
/*
- * Temporary partitions belonging to other sessions should have been- * disallowed at definition, but for paranoia's sake, let's double- * check.+ * A partitioned child table with 0 children is a dummy rel, so don't+ * bother creating planner objects for it.
*/
- if (RELATION_IS_OTHER_TEMP(childrel))- elog(ERROR, "temporary relation from another session found as partition");+ if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&+ RelationGetPartitionDesc(newrelation)->nparts == 0)+ {+ heap_close(newrelation, NoLock);+ continue;+ }- expand_single_inheritance_child(root, parentrte, parentRTindex,- parentrel, top_parentrc, childrel,- appinfos, &childrte, &childRTindex);+ childrel = add_inheritance_child_rel(root, parentrte, parentRTindex,+ parentrel, rootrc, newrelation);+ Assert(childrel != NULL);+ parentrel->part_rels[i] = childrel;- /* If this child is itself partitioned, recurse */- if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)- expand_partitioned_rtentry(root, childrte, childRTindex,- childrel, top_parentrc, lockmode,- appinfos);+ /* Close child relations, but keep locks */+ heap_close(newrelation, NoLock);+ }- /* Close child relation, but keep locks */- heap_close(childrel, NoLock);+ /*+ * Add junk columns needed by the row mark if any and also add the+ * relevant expressions to the root parent's reltarget.+ */+ if (rootrc)+ {+ List *tlist = add_rowmark_junk_columns(root, rootrc);++ build_base_rel_tlists(root, tlist);
}
}
/*
- * expand_single_inheritance_child- * Build a RangeTblEntry and an AppendRelInfo, if appropriate, plus- * maybe a PlanRowMark.+ * add_inheritance_child_rel+ * Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally+ * a RelOptInfo for an inheritance child relation.
*
- * We now expand the partition hierarchy level by level, creating a- * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each- * partitioned descendant acts as a parent of its immediate partitions.- * (This is a difference from what older versions of PostgreSQL did and what- * is still done in the case of table inheritance for unpartitioned tables,- * where the hierarchy is flattened during RTE expansion.)+ * The return value is the RelOptInfo that's added.
*
* PlanRowMarks still carry the top-parent's RTI, and the top-parent's
* allMarkTypes field still accumulates values from all descendents.
*
* "parentrte" and "parentRTindex" are immediate parent's RTE and
* RTI. "top_parentrc" is top parent's PlanRowMark.
- *- * The child RangeTblEntry and its RTI are returned in "childrte_p" and- * "childRTindex_p" resp.
*/
-static void-expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,- Index parentRTindex, Relation parentrel,- PlanRowMark *top_parentrc, Relation childrel,- List **appinfos, RangeTblEntry **childrte_p,- Index *childRTindex_p)+static RelOptInfo *+add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel,+ PlanRowMark *top_parentrc, Relation childrel)
{
Query *parse = root->parse;
- Oid parentOID = RelationGetRelid(parentrel);
Oid childOID = RelationGetRelid(childrel);
RangeTblEntry *childrte;
Index childRTindex;
AppendRelInfo *appinfo;
+ RelOptInfo *childrelopt;
/*
* Build an RTE for the child, and attach to query's rangetable list. We
* copy most fields of the parent's RTE, but replace relation OID and
- * relkind, and set inh = false. Also, set requiredPerms to zero since- * all required permissions checks are done on the original RTE. Likewise,- * set the child's securityQuals to empty, because we only want to apply- * the parent's RLS conditions regardless of what RLS properties+ * relkind, and set inh appropriately. Also, set requiredPerms to zero+ * since all required permissions checks are done on the original RTE.+ * Likewise, set the child's securityQuals to empty, because we only want+ * to apply the parent's RLS conditions regardless of what RLS properties
* individual children may have. (This is an intentional choice to make
* inherited RLS work like regular permissions checks.) The parent
* securityQuals will be propagated to children along with other base
* restriction clauses, so we don't need to do it here.
*/
childrte = copyObject(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)- childrte->inh = true;- else- childrte->inh = false;+ /*+ * A partitioned child will need to be expanded as an append parent+ * itself, so set its inh to true.+ */+ childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
childrte->requiredPerms = 0;
childrte->securityQuals = NIL;
parse->rtable = lappend(parse->rtable, childrte);
childRTindex = list_length(parse->rtable);
- *childRTindex_p = childRTindex;++ /* Create an AppendRelInfo and add it to planner's global list. */+ appinfo = make_append_rel_info(parentrel, parentrte,+ childrel->rd_att,+ childOID,+ childrel->rd_rel->reltype,+ childRTindex);+ root->append_rel_list = lappend(root->append_rel_list, appinfo);
/*
- * 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.+ * Translate the column permissions bitmaps to the child's attnums (we+ * have to build the translated_vars list before we can do this). But+ * if this is the parent table, leave copyObject's result alone.+ *+ * Note: we need to do this even though the executor won't run any+ * permissions checks on the child RTE. The insertedCols/updatedCols+ * bitmaps may be examined for trigger-firing purposes.
*/
- if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)+ if (childrte->relid != parentrte->relid)
{
- appinfo = makeNode(AppendRelInfo);- appinfo->parent_relid = parentRTindex;- appinfo->child_relid = childRTindex;- appinfo->parent_reltype = parentrel->rd_rel->reltype;- appinfo->child_reltype = childrel->rd_rel->reltype;- make_inh_translation_list(parentrel, childrel, childRTindex,- &appinfo->translated_vars);- appinfo->parent_reloid = parentOID;- *appinfos = lappend(*appinfos, appinfo);-- /*- * Translate the column permissions bitmaps to the child's attnums (we- * have to build the translated_vars list before we can do this). But- * if this is the parent table, leave copyObject's result alone.- *- * Note: we need to do this even though the executor won't run any- * permissions checks on the child RTE. The insertedCols/updatedCols- * bitmaps may be examined for trigger-firing purposes.- */- if (childOID != parentOID)- {- childrte->selectedCols = translate_col_privs(parentrte->selectedCols,- appinfo->translated_vars);- childrte->insertedCols = translate_col_privs(parentrte->insertedCols,- appinfo->translated_vars);- childrte->updatedCols = translate_col_privs(parentrte->updatedCols,- appinfo->translated_vars);- }+ childrte->selectedCols = translate_col_privs(parentrte->selectedCols,+ appinfo->translated_vars);+ childrte->insertedCols = translate_col_privs(parentrte->insertedCols,+ appinfo->translated_vars);+ childrte->updatedCols = translate_col_privs(parentrte->updatedCols,+ appinfo->translated_vars);
}
/*
@@ -1867,6 +1883,24 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
root->rowMarks = lappend(root->rowMarks, childrc);
}
++ /*+ * Add the RelOptInfo. Even though we may not really scan this relation+ * for reasons such as contradictory quals, we still need need to create+ * one, because for every RTE in the query's range table, there must be an+ * accompanying RelOptInfo.+ */++ /* First, store the RTE and appinfos into planner arrays. */+ 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;++ childrelopt = build_append_child_rel(root, parentrel, childRTindex);+ Assert(childrelopt != NULL);++ return childrelopt;
}
/*
@@ -1877,14 +1911,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
* For paranoia's sake, we match type/collation as well as attribute name.
*/
static void
-make_inh_translation_list(Relation oldrelation, Relation newrelation,- Index newvarno,- List **translated_vars)+make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc,+ Oid from_rel, Oid to_rel,+ Index newvarno, List **translated_vars)
{
List *vars = NIL;
- TupleDesc old_tupdesc = RelationGetDescr(oldrelation);- TupleDesc new_tupdesc = RelationGetDescr(newrelation);- Oid new_relid = RelationGetRelid(newrelation);
int oldnatts = old_tupdesc->natts;
int newnatts = new_tupdesc->natts;
int old_attno;
@@ -1914,7 +1945,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
* When we are generating the "translation list" for the parent table
* of an inheritance set, no need to search for matches.
*/
- if (oldrelation == newrelation)+ if (from_rel == to_rel)
{
vars = lappend(vars, makeVar(newvarno,
(AttrNumber) (old_attno + 1),
@@ -1940,10 +1971,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
{
HeapTuple newtup;
- newtup = SearchSysCacheAttName(new_relid, attname);+ newtup = SearchSysCacheAttName(to_rel, attname);
if (!newtup)
elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
- attname, RelationGetRelationName(newrelation));+ attname, get_rel_name(to_rel));
new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
ReleaseSysCache(newtup);
@@ -1953,10 +1984,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
/* Found it, check type and collation match */
if (atttypid != att->atttypid || atttypmod != att->atttypmod)
elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
- attname, RelationGetRelationName(newrelation));+ attname, get_rel_name(to_rel));
if (attcollation != att->attcollation)
elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
- attname, RelationGetRelationName(newrelation));+ attname, get_rel_name(to_rel));
vars = lappend(vars, makeVar(newvarno,
(AttrNumber) (new_attno + 1),
@@ -2023,6 +2054,255 @@ translate_col_privs(const Bitmapset *parent_privs,
}
/*
+ * build_append_child_rel+ * Build a RelOptInfo for child relation of an append rel+ *+ * After creating the RelOptInfo for the given child RT index, it goes on to+ * initialize some of its fields base on the parent RelOptInfo.+ *+ * If the quals in baserestrictinfo turns out to be self-contradictory, the+ * RelOptInfo is marked dummy before returning.+ */+static RelOptInfo *+build_append_child_rel(PlannerInfo *root,+ RelOptInfo *parent,+ Index childRTindex)+{+ RelOptInfo *childrel;+ RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];+ AppendRelInfo *appinfo = root->append_rel_array[childRTindex];+ List *childquals;+ ListCell *lc;+ bool have_const_false_cq;+ Index cq_min_security;++ /* Build the RelOptInfo. */+ childrel = build_simple_rel(root, childRTindex, parent);++ /*+ * Propagate lateral_relids and lateral_referencers from appendrel+ * parent rels to their child rels. We intentionally give each child rel+ * the same minimum parameterization, even though it's quite possible that+ * some don't reference all the lateral rels. This is because any append+ * path for the parent will have to have the same parameterization for+ * every child anyway, and there's no value in forcing extra+ * reparameterize_path() calls. Similarly, a lateral reference to the+ * parent prevents use of otherwise-movable join rels for each child.+ */+ childrel->direct_lateral_relids = parent->direct_lateral_relids;+ childrel->lateral_relids = parent->lateral_relids;+ childrel->lateral_referencers = parent->lateral_referencers;++ /*+ * We have to copy the parent's quals to the child, with appropriate+ * substitution of variables. However, only the baserestrictinfo+ * quals are needed before we can check for constraint exclusion; so+ * do that first and then check to see if we can disregard this child.+ *+ * The child rel's targetlist might contain non-Var expressions, which+ * means that substitution into the quals could produce opportunities+ * for const-simplification, and perhaps even pseudoconstant quals.+ * Therefore, transform each RestrictInfo separately to see if it+ * reduces to a constant or pseudoconstant. (We must process them+ * separately to keep track of the security level of each qual.)+ */+ childquals = false;+ cq_min_security = UINT_MAX;+ have_const_false_cq = false;+ foreach(lc, parent->baserestrictinfo)+ {+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);+ Node *childqual = (Node *) rinfo->clause;+ ListCell *lc2;++ Assert(IsA(rinfo, RestrictInfo));+ childqual = adjust_appendrel_attrs(root, childqual,+ 1, &appinfo);+ childqual = eval_const_expressions(root, childqual);+ /* check for flat-out constant */+ if (childqual && IsA(childqual, Const))+ {+ if (((Const *) childqual)->constisnull ||+ !DatumGetBool(((Const *) childqual)->constvalue))+ {+ /* Restriction reduces to constant FALSE or NULL */+ have_const_false_cq = true;+ break;+ }+ /* Restriction reduces to constant TRUE, so drop it */+ continue;+ }+ /* might have gotten an AND clause, if so flatten it */+ foreach(lc2, make_ands_implicit((Expr *) childqual))+ {+ Node *onecq = (Node *) lfirst(lc2);+ bool pseudoconstant;++ /* check for pseudoconstant (no Vars or volatile functions) */+ pseudoconstant =+ !contain_vars_of_level(onecq, 0) &&+ !contain_volatile_functions(onecq);+ if (pseudoconstant)+ {+ /* tell createplan.c to check for gating quals */+ root->hasPseudoConstantQuals = true;+ }+ /* reconstitute RestrictInfo with appropriate properties */+ childquals = lappend(childquals,+ make_restrictinfo((Expr *) onecq,+ rinfo->is_pushed_down,+ rinfo->outerjoin_delayed,+ pseudoconstant,+ rinfo->security_level,+ NULL, NULL, NULL));+ cq_min_security = Min(cq_min_security, rinfo->security_level);+ }+ }++ /*+ * In addition to the quals inherited from the parent, we might+ * have securityQuals associated with this particular child node.+ * (Currently this can only happen in appendrels originating from+ * UNION ALL; inheritance child tables don't have their own+ * securityQuals.) Pull any such securityQuals up into the+ * baserestrictinfo for the child. This is similar to+ * process_security_barrier_quals() for the parent rel, except+ * that we can't make any general deductions from such quals,+ * since they don't hold for the whole appendrel.+ */+ if (childRTE->securityQuals)+ {+ Index security_level = 0;++ foreach(lc, childRTE->securityQuals)+ {+ List *qualset = (List *) lfirst(lc);+ ListCell *lc2;++ foreach(lc2, qualset)+ {+ Expr *qual = (Expr *) lfirst(lc2);++ /*+ * not likely that we'd see constants here, so no+ * check+ */+ childquals = lappend(childquals,+ make_restrictinfo(qual,+ true,+ false,+ false,+ security_level,+ NULL, NULL,+ NULL));+ cq_min_security = Min(cq_min_security, security_level);+ }+ security_level++;+ }+ Assert(security_level <= root->qual_security_level);+ }++ /* Set child's version of baserestrictinfo. */+ childrel->baserestrictinfo = childquals;+ childrel->baserestrict_min_security = cq_min_security;++ if (have_const_false_cq)+ {+ /*+ * Some restriction clause reduced to constant FALSE or NULL after+ * substitution, so this child need not be scanned.+ */+ set_dummy_rel_pathlist(childrel);+ }++ return childrel;+}++/*+ * add_rowmark_junk_columns+ * Add necessary junk columns for rowmarked inheritance parent rel.+ *+ * These values are needed for locking of rels selected FOR UPDATE/SHARE, and+ * to do EvalPlanQual rechecking. See comments for PlanRowMark in+ * plannodes.h.+ */+static List *+add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc)+{+ List *tlist = root->processed_tlist;+ Var *var;+ char resname[32];+ TargetEntry *tle;++ if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))+ {+ /* Need to fetch TID */+ var = makeVar(rc->rti,+ SelfItemPointerAttributeNumber,+ TIDOID,+ -1,+ InvalidOid,+ 0);+ snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);+ }+ if (rc->allMarkTypes & (1 << ROW_MARK_COPY))+ {+ /* Need the whole row as a junk var */+ var = makeWholeRowVar(root->simple_rte_array[rc->rti],+ rc->rti,+ 0,+ false);+ snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);+ }++ /* For inheritance cases, always fetch the tableoid too. */+ var = makeVar(rc->rti,+ TableOidAttributeNumber,+ OIDOID,+ -1,+ InvalidOid,+ 0);+ snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);++ return tlist;+}++AppendRelInfo *+make_append_rel_info(RelOptInfo *parentrel, RangeTblEntry *parentrte,+ TupleDesc childdesc, Oid childoid, Oid childreltype,+ Index childRTindex)+{+ AppendRelInfo *appinfo = makeNode(AppendRelInfo);++ appinfo->parent_relid = parentrel->relid;+ appinfo->child_relid = childRTindex;+ appinfo->parent_reltype = parentrel->reltype;+ appinfo->child_reltype = childreltype;+ make_inh_translation_list(parentrel->tupdesc, childdesc,+ parentrte->relid, childoid,+ childRTindex,+ &appinfo->translated_vars);+ appinfo->parent_reloid = parentrte->relid;++ return appinfo;+}++/*
* adjust_appendrel_attrs
* Copy the specified query or expression and translate Vars referring to a
* parent rel to refer to the corresponding child rel instead. We also
diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefileindex c54d0a690d..da9ccf32b4 100644--- a/src/backend/optimizer/util/Makefile+++ b/src/backend/optimizer/util/Makefile@@ -13,6 +13,6 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = clauses.o joininfo.o orclauses.o pathnode.o placeholder.o \
- plancat.o predtest.o relnode.o restrictinfo.o tlist.o var.o+ plancat.o predtest.o relnode.o restrictinfo.o tlist.o var.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.cindex ed0953f9e1..ce8a8fb78e 100644--- a/src/backend/optimizer/util/plancat.c+++ b/src/backend/optimizer/util/plancat.c@@ -106,7 +106,7 @@ static void set_baserel_partition_key_exprs(Relation relation,
*/
void
get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
- RelOptInfo *rel)+ Bitmapset *updatedCols, RelOptInfo *rel)
{
Index varno = rel->relid;
Relation relation;
@@ -444,11 +444,32 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
get_relation_foreign_keys(root, rel, relation, inhparent);
/*
- * Collect info about relation's partitioning scheme, if any. Only- * inheritance parents may be partitioned.+ * Collect some additional information for inheritance parents.
*/
- if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)- set_relation_partition_info(root, rel, relation);+ if (inhparent)+ {+ /*+ * We'll need the TupleDesc when initializing the child relation.+ * A copy is being made because concurrent changes might drop+ * the relcache entry. That's possible because ALTER TABLE+ * child_table NO INHERIT parent_table only requires an+ * AccessShareLock on parent_table.+ */+ rel->tupdesc = CreateTupleDescCopy(RelationGetDescr(relation));+ rel->reltype = RelationGetForm(relation)->reltype;++ /*+ * If partitioned, also save the information of partitioning scheme,+ * and whether the query updates any of the partition key columns.+ */+ if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)+ {+ set_relation_partition_info(root, rel, relation);+ if (!root->partColsUpdated)+ root->partColsUpdated =+ has_partition_attrs(relation, updatedCols, NULL);+ }+ }
heap_close(relation, NoLock);
@@ -1854,16 +1875,20 @@ set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
Relation relation)
{
PartitionDesc partdesc;
- PartitionKey partkey;
Assert(relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
partdesc = RelationGetPartitionDesc(relation);
- partkey = RelationGetPartitionKey(relation);
rel->part_scheme = find_partition_scheme(root, relation);
Assert(partdesc != NULL && rel->part_scheme != NULL);
- rel->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
rel->nparts = partdesc->nparts;
++ /*+ * Since we must've taken a lock on the table, it's okay to simply copy+ * the pointers to relcache data here.+ */+ rel->part_oids = partdesc->oids;+ rel->boundinfo = partdesc->boundinfo;
set_baserel_partition_key_exprs(relation, rel);
rel->partition_qual = RelationGetPartitionQual(relation);
}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.cindex 29ba19349f..c23db9d78d 100644--- a/src/backend/optimizer/util/relnode.c+++ b/src/backend/optimizer/util/relnode.c@@ -130,6 +130,37 @@ setup_append_rel_array(PlannerInfo *root)
}
}
+/* Expand the PlannerInfo arrays by add_size members and zero-init it. */+void+expand_planner_arrays(PlannerInfo *root, int add_size)+{+ int new_size = root->simple_rel_array_size + add_size;++ root->simple_rte_array = (RangeTblEntry **)+ repalloc(root->simple_rte_array,+ sizeof(RangeTblEntry *) * new_size);+ root->simple_rel_array = (RelOptInfo **)+ repalloc(root->simple_rel_array,+ sizeof(RelOptInfo *) * new_size);+ if (root->append_rel_array)+ root->append_rel_array = (AppendRelInfo **)+ repalloc(root->append_rel_array,+ sizeof(AppendRelInfo *) * new_size);+ else+ root->append_rel_array = (AppendRelInfo **)+ palloc0(sizeof(AppendRelInfo *) *+ new_size);++ /* Set the contents of just allocated memory to 0. */+ MemSet(root->simple_rte_array + root->simple_rel_array_size,+ 0, sizeof(RangeTblEntry *) * add_size);+ MemSet(root->simple_rel_array + root->simple_rel_array_size,+ 0, sizeof(RelOptInfo *) * add_size);+ MemSet(root->append_rel_array + root->simple_rel_array_size,+ 0, sizeof(AppendRelInfo *) * add_size);+ root->simple_rel_array_size = new_size;+}+
/*
* build_simple_rel
* Construct a new RelOptInfo for a base relation or 'other' relation.
@@ -238,7 +269,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
{
case RTE_RELATION:
/* Table --- retrieve statistics from the system catalogs */
- get_relation_info(root, rte->relid, rte->inh, rel);+ get_relation_info(root, rte->relid, rte->inh, rte->updatedCols,+ rel);
break;
case RTE_SUBQUERY:
case RTE_FUNCTION:
@@ -279,52 +311,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
root->qual_security_level = Max(root->qual_security_level,
list_length(rte->securityQuals));
- /*- * If this rel is an appendrel parent, recurse to build "other rel"- * RelOptInfos for its children. They are "other rels" because they are- * not in the main join tree, but we will need RelOptInfos to plan access- * to them.- */- if (rte->inh)- {- ListCell *l;- int nparts = rel->nparts;- int cnt_parts = 0;-- if (nparts > 0)- rel->part_rels = (RelOptInfo **)- palloc(sizeof(RelOptInfo *) * nparts);-- foreach(l, root->append_rel_list)- {- AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);- RelOptInfo *childrel;-- /* append_rel_list contains all append rels; ignore others */- if (appinfo->parent_relid != relid)- continue;-- childrel = build_simple_rel(root, appinfo->child_relid,- rel);-- /* Nothing more to do for an unpartitioned table. */- if (!rel->part_scheme)- continue;-- /*- * The order of partition OIDs in append_rel_list is the same as- * the order in the PartitionDesc, so the order of part_rels will- * also match the PartitionDesc. See expand_partitioned_rtentry.- */- Assert(cnt_parts < nparts);- rel->part_rels[cnt_parts] = childrel;- cnt_parts++;- }-- /* We should have seen all the child partitions. */- Assert(cnt_parts == nparts);- }-
return rel;
}
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.cindex d6ca03de4a..bf17ae22ba 100644--- a/src/backend/partitioning/partprune.c+++ b/src/backend/partitioning/partprune.c@@ -45,6 +45,7 @@
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/planner.h"
#include "optimizer/predtest.h"
@@ -436,17 +437,23 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
* is, not pruned already).
*/
subplan_map = (int *) palloc(nparts * sizeof(int));
+ memset(subplan_map, -1, nparts * sizeof(int));
subpart_map = (int *) palloc(nparts * sizeof(int));
+ memset(subpart_map, -1, nparts * sizeof(int));
present_parts = NULL;
for (i = 0; i < nparts; i++)
{
RelOptInfo *partrel = subpart->part_rels[i];
- int subplanidx = relid_subplan_map[partrel->relid] - 1;- int subpartidx = relid_subpart_map[partrel->relid] - 1;+ int subplanidx;+ int subpartidx;- subplan_map[i] = subplanidx;- subpart_map[i] = subpartidx;+ /* Skip processing pruned partitions. */+ if (partrel == NULL)+ continue;++ subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;+ subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
if (subplanidx >= 0)
{
present_parts = bms_add_member(present_parts, i);
@@ -540,23 +547,20 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory)
/*
* prune_append_rel_partitions
- * Returns RT indexes of the minimum set of child partitions which must- * be scanned to satisfy rel's baserestrictinfo quals.+ * Returns indexes into rel->part_rels of the minimum set of child+ * partitions which must be scanned to satisfy rel's baserestrictinfo+ * quals.
*
* Callers must ensure that 'rel' is a partitioned table.
*/
-Relids+Bitmapset *
prune_append_rel_partitions(RelOptInfo *rel)
{
- Relids result;
List *clauses = rel->baserestrictinfo;
List *pruning_steps;
bool contradictory;
PartitionPruneContext context;
- Bitmapset *partindexes;- int i;- Assert(clauses != NIL);
Assert(rel->part_scheme != NULL);
/* If there are no partitions, return the empty set */
@@ -564,6 +568,13 @@ prune_append_rel_partitions(RelOptInfo *rel)
return NULL;
/*
+ * If pruning is disabled or if there are no clauses to prune with,+ * return all partitions.+ */+ if (!enable_partition_pruning || clauses == NIL)+ return bms_add_range(NULL, 0, rel->nparts - 1);++ /*
* Process clauses. If the clauses are found to be contradictory, we can
* return the empty set.
*/
@@ -590,15 +601,7 @@ prune_append_rel_partitions(RelOptInfo *rel)
context.evalexecparams = false;
/* Actual pruning happens here. */
- partindexes = get_matching_partitions(&context, pruning_steps);-- /* Add selected partitions' RT indexes to result. */- i = -1;- result = NULL;- while ((i = bms_next_member(partindexes, i)) >= 0)- result = bms_add_member(result, rel->part_rels[i]->relid);-- return result;+ return get_matching_partitions(&context, pruning_steps);
}
/*
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.hindex 29339d6c72..6b614e4794 100644--- a/src/include/nodes/relation.h+++ b/src/include/nodes/relation.h@@ -15,6 +15,7 @@
#define RELATION_H
#include "access/sdir.h"
+#include "access/tupdesc.h"
#include "fmgr.h"
#include "lib/stringinfo.h"
#include "nodes/params.h"
@@ -710,6 +711,7 @@ typedef struct RelOptInfo
int nparts; /* number of partitions */
struct PartitionBoundInfoData *boundinfo; /* Partition bounds */
List *partition_qual; /* partition constraint */
+ Oid *part_oids; /* partition OIDs */
struct RelOptInfo **part_rels; /* Array of RelOptInfos of partitions,
* stored in the same order of bounds */
List **partexprs; /* Non-nullable partition key expressions. */
@@ -719,6 +721,12 @@ typedef struct RelOptInfo
Index inh_root_parent; /* For otherrels, this is the RT index of
* inheritance table mentioned in the query
* from which this relation originated */
+ /*+ * Set only if this is an inheritance parent relation. This information+ * is needed when initializing the planning info for children.+ */+ TupleDesc tupdesc; /* A "copy" of the table's tuple desriptor */+ Oid reltype; /* Table's reltype */
} RelOptInfo;
/*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.hindex 81abcf53a8..b1baa3117a 100644--- a/src/include/optimizer/pathnode.h+++ b/src/include/optimizer/pathnode.h@@ -261,6 +261,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 RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.hindex 7d53cbbb87..edaf2a3b4f 100644--- a/src/include/optimizer/plancat.h+++ b/src/include/optimizer/plancat.h@@ -26,7 +26,7 @@ extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook;
extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
- bool inhparent, RelOptInfo *rel);+ bool inhparent, Bitmapset *updatedCols, RelOptInfo *rel);
extern List *infer_arbiter_indexes(PlannerInfo *root);
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.hindex 38608770a2..e8e21e2ed5 100644--- a/src/include/optimizer/prep.h+++ b/src/include/optimizer/prep.h@@ -38,7 +38,7 @@ extern Expr *canonicalize_qual(Expr *qual, bool is_check);
/*
* prototypes for preptlist.c
*/
-extern List *preprocess_targetlist(PlannerInfo *root);+extern List *preprocess_targetlist(PlannerInfo *root, bool inheritance_update);
extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
@@ -46,12 +46,16 @@ extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
* prototypes for prepunion.c
*/
extern RelOptInfo *plan_set_operations(PlannerInfo *root);
--extern void expand_inherited_tables(PlannerInfo *root);-+extern void expand_append_rel(PlannerInfo *root,+ RelOptInfo *rel,+ RangeTblEntry *rte,+ Index rti);
extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
int nappinfos, AppendRelInfo **appinfos);
-+extern AppendRelInfo *make_append_rel_info(RelOptInfo *parentrel,+ RangeTblEntry *parentrte,+ TupleDesc childdesc, Oid childoid, Oid childreltype,+ Index childRTindex);
extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
Relids child_relids,
Relids top_parent_relids);
@@ -64,5 +68,4 @@ extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
Relids left_relids, Relids right_relids);
extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
Relids child_relids, Relids top_parent_relids);
-
#endif /* PREP_H */
diff --git a/src/include/partitioning/partprune.h b/src/include/partitioning/partprune.hindex e07aaaf798..ac0a979010 100644--- a/src/include/partitioning/partprune.h+++ b/src/include/partitioning/partprune.h@@ -76,7 +76,7 @@ extern PartitionPruneInfo *make_partition_pruneinfo(PlannerInfo *root,
List *subpaths,
List *partitioned_rels,
List *prunequal);
-extern Relids prune_append_rel_partitions(RelOptInfo *rel);+extern Bitmapset *prune_append_rel_partitions(RelOptInfo *rel);
extern Bitmapset *get_matching_partitions(PartitionPruneContext *context,
List *pruning_steps);
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.outindex 1f5378080d..73f684f992 100644--- a/src/test/regress/expected/join.out+++ b/src/test/regress/expected/join.out@@ -5547,29 +5547,29 @@ select t1.b, ss.phv from join_ut1 t1 left join lateral
(select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv
from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss
on t1.a = ss.t2a order by t1.a;
- QUERY PLAN -------------------------------------------------------------------+ QUERY PLAN +--------------------------------------------------------------------
Sort
- Output: t1.b, (LEAST(t1.a, t2.a, t3.a)), t1.a+ Output: t1.b, (LEAST(t1.a, t2_1.a, t3.a)), t1.a
Sort Key: t1.a
-> Nested Loop Left Join
- Output: t1.b, (LEAST(t1.a, t2.a, t3.a)), t1.a+ Output: t1.b, (LEAST(t1.a, t2_1.a, t3.a)), t1.a
-> Seq Scan on public.join_ut1 t1
Output: t1.a, t1.b, t1.c
-> Hash Join
- Output: t2.a, LEAST(t1.a, t2.a, t3.a)- Hash Cond: (t3.b = t2.a)+ Output: t2_1.a, LEAST(t1.a, t2_1.a, t3.a)+ Hash Cond: (t3.b = t2_1.a)
-> Seq Scan on public.join_ut1 t3
Output: t3.a, t3.b, t3.c
-> Hash
- Output: t2.a+ Output: t2_1.a
-> Append
- -> Seq Scan on public.join_pt1p1p1 t2- Output: t2.a- Filter: (t1.a = t2.a)- -> Seq Scan on public.join_pt1p2 t2_1+ -> Seq Scan on public.join_pt1p1p1 t2_1
Output: t2_1.a
Filter: (t1.a = t2_1.a)
+ -> Seq Scan on public.join_pt1p2 t2+ Output: t2.a+ Filter: (t1.a = t2.a)
(21 rows)
select t1.b, ss.phv from join_ut1 t1 left join lateral
diff --git a/src/test/regress/expected/partition_aggregate.out b/src/test/regress/expected/partition_aggregate.outindex 6bc106831e..1450cef057 100644--- a/src/test/regress/expected/partition_aggregate.out+++ b/src/test/regress/expected/partition_aggregate.out@@ -144,7 +144,7 @@ SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c;
QUERY PLAN
--------------------------------
HashAggregate
- Group Key: pagg_tab.c+ Group Key: c
-> Result
One-Time Filter: false
(4 rows)
@@ -159,7 +159,7 @@ SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c;
QUERY PLAN
--------------------------------
GroupAggregate
- Group Key: pagg_tab.c+ Group Key: c
-> Result
One-Time Filter: false
(4 rows)
--
2.11.0
From 68c565448b11e4c1294411b1841a6d527bdb9726 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 7 Nov 2018 16:51:31 +0900
Subject: [PATCH v5 4/6] Move append expansion code into its own file
This commit moves expand_append_rel and underlings to
optimizer/utils/append.c.
Also, all of the AppendRelInfo based
expression manipulation routines are moved to
optimizer/utils/appendinfo.c.
This commit only moves the code and contains no functional changes.
---
src/backend/optimizer/path/allpaths.c | 2 +
src/backend/optimizer/path/equivclass.c | 1 +
src/backend/optimizer/path/joinrels.c | 1 +
src/backend/optimizer/plan/planner.c | 1 +
src/backend/optimizer/prep/prepunion.c | 1516 -------------------------------
src/backend/optimizer/util/Makefile | 5 +-
src/backend/optimizer/util/append.c | 719 +++++++++++++++
src/backend/optimizer/util/appendinfo.c | 851 +++++++++++++++++
src/backend/optimizer/util/pathnode.c | 1 +
src/backend/optimizer/util/relnode.c | 1 +
src/backend/partitioning/partprune.c | 1 +
src/include/optimizer/append.h | 28 +
src/include/optimizer/appendinfo.h | 43 +
src/include/optimizer/prep.h | 21 -
14 files changed, 1652 insertions(+), 1539 deletions(-)
create mode 100644 src/backend/optimizer/util/append.c
create mode 100644 src/backend/optimizer/util/appendinfo.c
create mode 100644 src/include/optimizer/append.h
create mode 100644 src/include/optimizer/appendinfo.h
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.cindex 6ee800d84a..0d051681e6 100644--- a/src/backend/optimizer/path/allpaths.c+++ b/src/backend/optimizer/path/allpaths.c@@ -30,6 +30,8 @@
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
+#include "optimizer/append.h"+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/geqo.h"
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.cindex b22b36ec0e..5ace28eab0 100644--- a/src/backend/optimizer/path/equivclass.c+++ b/src/backend/optimizer/path/equivclass.c@@ -22,6 +22,7 @@
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.cindex da0831de4e..6e321ec9e7 100644--- a/src/backend/optimizer/path/joinrels.c+++ b/src/backend/optimizer/path/joinrels.c@@ -15,6 +15,7 @@
#include "postgres.h"
#include "miscadmin.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.cindex 84746a9bc9..216cadfba9 100644--- a/src/backend/optimizer/plan/planner.c+++ b/src/backend/optimizer/plan/planner.c@@ -39,6 +39,7 @@
#include "nodes/print.h"
#endif
#include "nodes/relation.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.cindex c6817757e7..70f37c593f 100644--- a/src/backend/optimizer/prep/prepunion.c+++ b/src/backend/optimizer/prep/prepunion.c@@ -12,11 +12,6 @@
* case, but most of the heavy lifting for that is done elsewhere,
* notably in prepjointree.c and allpaths.c.
*
- * There is also some code here to support planning of queries that use- * inheritance (SELECT FROM foo*). Inheritance trees are converted into- * append relations, and thenceforth share code with the UNION ALL case.- *- *
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -39,32 +34,21 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
-#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
-#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
-#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parsetree.h"
-#include "partitioning/partprune.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
#include "utils/syscache.h"
-typedef struct-{- PlannerInfo *root;- int nappinfos;- AppendRelInfo **appinfos;-} adjust_appendrel_attrs_context;-
static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
List *colTypes, List *colCollations,
bool junkOK,
@@ -103,31 +87,6 @@ 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,- Index rti, RelOptInfo *rel);-static void expand_partitioned_rtentry(PlannerInfo *root,- RangeTblEntry *parentrte,- Index parentRTindex, RelOptInfo *parentrel);-static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root,- RangeTblEntry *parentrte,- Index parentRTindex, RelOptInfo *parentrel,- PlanRowMark *top_parentrc, Relation childrel);-static void make_inh_translation_list(TupleDesc old_tupdesc,- TupleDesc new_tupdesc,- Oid from_rel, Oid to_rel,- Index newvarno, List **translated_vars);-static RelOptInfo *build_append_child_rel(PlannerInfo *root,- RelOptInfo *parent,- Index childRTindex);-static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc);-static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,- List *translated_vars);-static Node *adjust_appendrel_attrs_mutator(Node *node,- adjust_appendrel_attrs_context *context);-static Relids adjust_child_relids(Relids relids, int nappinfos,- AppendRelInfo **appinfos);-static List *adjust_inherited_tlist(List *tlist,- AppendRelInfo *context);
/*
@@ -1464,1478 +1423,3 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Assert(lg == NULL);
return grouplist;
}
---/*- * expand_append_rel- * This initializes RelOptInfos for an appendrel's child relations.- *- * 'rel' is the appendrel parent, whose range table entry ('rte') has been- * marked to require adding children. An appendrel parent could either- * be a subquery (if we flattened UNION ALL query) or a table that's known- * to have inheritance children. The latter consists of both regular- * inheritance parents and partitioned tables.- *- * For a subquery parent, there is not much to be done here because the- * children's RTEs are already present in the query, so we just initialize- * RelOptInfos for them. Also, the AppendRelInfos for child subqueries- * have already been added.- *- * For tables, we need to add the children to the range table and initialize- * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them. For- * a partitioned parent, we only add the children remaining after pruning.- * For regular inheritance parents, we find the children using- * find_all_inheritors and add all of them.- *- * If it turns out that there are no children, then we set rte->inh to false- * to let the caller know that only the parent table needs to be scanned. The- * caller can accordingly switch to a non-Append path. For a partitioned- * parent, that means an empty relation because parents themselves contain no- * data.- *- * For the regular inheritance case, the parent also gets another RTE with- * inh = false to represent it as an appendrel child. The original RTE is- * considered to represent the whole inheritance set.- */-void-expand_append_rel(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,- Index rti)-{- Assert(rte->inh);- /* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */- Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY);-- /*- * UNION ALL children already got RTEs and AppendRelInfos, so just build- * RelOptInfos and return.- *- * It might be a bit odd that this code is in this, because there is- * nothing to expand really.- */- if (rte->rtekind == RTE_SUBQUERY)- {- ListCell *l;-- /*- * We don't need to use expand_planner_arrays in this case, because- * no new child RTEs are created. setup_simple_rel_arrays() and- * setup_append_rel_array would've considered these child RTEs when- * allocating space for various arrays.- */- foreach(l, root->append_rel_list)- {- AppendRelInfo *appinfo = lfirst(l);- Index childRTindex = appinfo->child_relid;-- if (appinfo->parent_relid != rti)- continue;-- Assert(childRTindex < root->simple_rel_array_size);- Assert(root->simple_rte_array[childRTindex] != NULL);-- /*- * We set the correct value of baserestricinfo and- * baserestrict_min_security below.- */- root->simple_rel_array[childRTindex] =- build_append_child_rel(root, rel, appinfo->child_relid);- }- }- else- {- Assert(rte->rtekind == RTE_RELATION);- Assert(has_subclass(rte->relid));-- /*- * The rewriter should already have obtained an appropriate lock on- * each relation named in the query. However, for each child relation- * we add to the query, we must obtain an appropriate lock, because- * this will be the first use of those relations in the- * parse/rewrite/plan pipeline. Child rels should use the same- * lockmode as their parent.- */- Assert(rte->rellockmode != NoLock);-- if (rte->relkind == RELKIND_PARTITIONED_TABLE)- expand_partitioned_rtentry(root, rte, rti, rel);- else- expand_inherited_rtentry(root, rte, rti, rel);- }-}--/*- * expand_inherited_rtentry- * Add entries for all the child tables to the query's rangetable, and- * build AppendRelInfo nodes for all the child tables and add them to- * root->append_rel_list.- *- * Note that the original RTE is considered to represent the whole- * inheritance set. The first of the generated RTEs is an RTE for the same- * table, but with inh = false, to represent the parent table in its role- * as a simple member of the inheritance set.- *- * A childless table is never considered to be an inheritance set. For- * regular inheritance, a parent RTE must always have at least two associated- * AppendRelInfos: one corresponding to the parent table as a simple member of- * inheritance set and one or more corresponding to the actual children.- */-static void-expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti,- RelOptInfo *rel)-{- Oid parentOID;- PlanRowMark *oldrc;- LOCKMODE lockmode = rte->rellockmode;- List *inhOIDs;- ListCell *l;- int num_children;- int num_children_added = 0;-- Assert(rte->rtekind == RTE_RELATION);- Assert(lockmode != NoLock);- parentOID = rte->relid;-- /* Scan for all members of inheritance set, acquire needed locks */- inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);-- /*- * Check that there's at least one descendant, else treat as no-child- * case. This could happen despite above has_subclass() check, if table- * once had a child but no longer does.- */- num_children = list_length(inhOIDs);- if (num_children < 2)- {- /* Clear flag before returning */- rte->inh = false;- return;- }-- /*- * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks- * should've set isParent = true. We'll generate a new PlanRowMark for- * each child.- */- oldrc = get_plan_rowmark(root->rowMarks, rti);- Assert(oldrc == NULL || oldrc->isParent);-- /*- * Must expand PlannerInfo arrays by num_children before we can add- * children.- */- expand_planner_arrays(root, num_children);-- foreach(l, inhOIDs)- {- Oid childOID = lfirst_oid(l);- Relation newrelation;- RelOptInfo *childrel;-- /* Already locked above. */- newrelation = heap_open(childOID, NoLock);-- /*- * It is possible that the parent table has children that are temp- * tables of other backends. We cannot safely access such tables- * (because of buffering issues), and the best thing to do seems- * to be to silently ignore them.- */- if (RELATION_IS_OTHER_TEMP(newrelation))- {- heap_close(newrelation, lockmode);- continue;- }-- childrel = add_inheritance_child_rel(root, rte, rti, rel, oldrc,- newrelation);- Assert(childrel != NULL);-- /* Close child relations, but keep locks */- heap_close(newrelation, NoLock);- num_children_added++;- }-- /*- * If all children, including the parent (as child rel), were- * excluded, mark the parent rel as empty. If all the children were temp- * tables, pretend it's a non-inheritance situation; we don't need Append- * node in that case. The duplicate RTE we added for the parent table is- * harmless, so we don't bother to get rid of it; ditto for the useless- * PlanRowMark node.- */- if (num_children_added == 0)- mark_dummy_rel(rel);- else if (num_children_added == 1)- rte->inh = false;-- /*- * Add junk columns needed by the row mark if any and also add the- * relevant expressions to the root parent's reltarget.- */- if (oldrc)- {- List *tlist = add_rowmark_junk_columns(root, oldrc);-- build_base_rel_tlists(root, tlist);- }-}--/*- * expand_partitioned_rtentry- * Prunes unnecessary partitions of a partitioned table and adds- * remaining ones to the Query and the PlannerInfo- *- * Partitions are added to the query in order in which they are found in- * the parent's PartitionDesc.- *- * Note: even though only the unpruned partitions will be added to the- * resulting plan, this still locks *all* partitions via find_all_inheritors- * in order to avoid partitions being locked in a different order than other- * places in the backend that may lock partitions.- */-static void-expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,- Index parentRTindex, RelOptInfo *parentrel)-{- LOCKMODE lockmode = parentrte->rellockmode;- PlanRowMark *rootrc = NULL;- int i;- Bitmapset *partindexes;- Index rootParentRTindex = parentrel->inh_root_parent > 0 ?- parentrel->inh_root_parent :- parentRTindex;-- /* If root partitioned table, lock *all* partitions in the tree. */- if (parentRTindex == rootParentRTindex)- (void) find_all_inheritors(parentrte->relid, lockmode, NULL);-- /*- * Initialize partitioned_child_rels to contain this RT index.- *- * Note that during the set_append_rel_pathlist() phase, values of the- * indexes of partitioned relations that appear down in the tree will be- * bubbled up into root parent's list so that when we've created Paths for- * all the children, the root table's list will contain all such indexes.- */- parentrel->partitioned_child_rels = list_make1_int(parentRTindex);-- /* Perform pruning. */- partindexes = prune_append_rel_partitions(parentrel);-- /* Must expand PlannerInfo arrays before we can add children. */- expand_planner_arrays(root, bms_num_members(partindexes));-- /*- * For partitioned tables, we also store the partition RelOptInfo- * pointers in the parent's RelOptInfo.- */- parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *- parentrel->nparts);-- rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex);- Assert(rootrc == NULL || rootrc->isParent);- i = -1;- while ((i = bms_next_member(partindexes, i)) >= 0)- {- Oid childOID = parentrel->part_oids[i];- Relation newrelation;- RelOptInfo *childrel;-- /* Already locked above. */- newrelation = heap_open(childOID, NoLock);- Assert(!RELATION_IS_OTHER_TEMP(newrelation));-- /*- * A partitioned child table with 0 children is a dummy rel, so don't- * bother creating planner objects for it.- */- if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&- RelationGetPartitionDesc(newrelation)->nparts == 0)- {- heap_close(newrelation, NoLock);- continue;- }-- childrel = add_inheritance_child_rel(root, parentrte, parentRTindex,- parentrel, rootrc, newrelation);- Assert(childrel != NULL);- parentrel->part_rels[i] = childrel;-- /* Close child relations, but keep locks */- heap_close(newrelation, NoLock);- }-- /*- * Add junk columns needed by the row mark if any and also add the- * relevant expressions to the root parent's reltarget.- */- if (rootrc)- {- List *tlist = add_rowmark_junk_columns(root, rootrc);-- build_base_rel_tlists(root, tlist);- }-}--/*- * add_inheritance_child_rel- * Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally- * a RelOptInfo for an inheritance child relation.- *- * The return value is the RelOptInfo that's added.- *- * PlanRowMarks still carry the top-parent's RTI, and the top-parent's- * allMarkTypes field still accumulates values from all descendents.- *- * "parentrte" and "parentRTindex" are immediate parent's RTE and- * RTI. "top_parentrc" is top parent's PlanRowMark.- */-static RelOptInfo *-add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte,- Index parentRTindex, RelOptInfo *parentrel,- PlanRowMark *top_parentrc, Relation childrel)-{- Query *parse = root->parse;- Oid childOID = RelationGetRelid(childrel);- RangeTblEntry *childrte;- Index childRTindex;- AppendRelInfo *appinfo;- RelOptInfo *childrelopt;-- /*- * Build an RTE for the child, and attach to query's rangetable list. We- * copy most fields of the parent's RTE, but replace relation OID and- * relkind, and set inh appropriately. Also, set requiredPerms to zero- * since all required permissions checks are done on the original RTE.- * Likewise, set the child's securityQuals to empty, because we only want- * to apply the parent's RLS conditions regardless of what RLS properties- * individual children may have. (This is an intentional choice to make- * inherited RLS work like regular permissions checks.) The parent- * securityQuals will be propagated to children along with other base- * restriction clauses, so we don't need to do it here.- */- childrte = copyObject(parentrte);- childrte->relid = childOID;- childrte->relkind = childrel->rd_rel->relkind;- /*- * A partitioned child will need to be expanded as an append parent- * itself, so set its inh to true.- */- childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE);- childrte->requiredPerms = 0;- childrte->securityQuals = NIL;- parse->rtable = lappend(parse->rtable, childrte);- childRTindex = list_length(parse->rtable);-- /* Create an AppendRelInfo and add it to planner's global list. */- appinfo = make_append_rel_info(parentrel, parentrte,- childrel->rd_att,- childOID,- childrel->rd_rel->reltype,- childRTindex);- root->append_rel_list = lappend(root->append_rel_list, appinfo);-- /*- * Translate the column permissions bitmaps to the child's attnums (we- * have to build the translated_vars list before we can do this). But- * if this is the parent table, leave copyObject's result alone.- *- * Note: we need to do this even though the executor won't run any- * permissions checks on the child RTE. The insertedCols/updatedCols- * bitmaps may be examined for trigger-firing purposes.- */- if (childrte->relid != parentrte->relid)- {- childrte->selectedCols = translate_col_privs(parentrte->selectedCols,- appinfo->translated_vars);- childrte->insertedCols = translate_col_privs(parentrte->insertedCols,- appinfo->translated_vars);- childrte->updatedCols = translate_col_privs(parentrte->updatedCols,- appinfo->translated_vars);- }-- /*- * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.- */- if (top_parentrc)- {- PlanRowMark *childrc = makeNode(PlanRowMark);-- childrc->rti = childRTindex;- childrc->prti = top_parentrc->rti;- childrc->rowmarkId = top_parentrc->rowmarkId;- /* Reselect rowmark type, because relkind might not match parent */- childrc->markType = select_rowmark_type(childrte,- top_parentrc->strength);- childrc->allMarkTypes = (1 << childrc->markType);- childrc->strength = top_parentrc->strength;- childrc->waitPolicy = top_parentrc->waitPolicy;-- /*- * We mark RowMarks for partitioned child tables as parent RowMarks so- * that the executor ignores them (except their existence means that- * the child tables be locked using appropriate mode).- */- childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);-- /* Include child's rowmark type in top parent's allMarkTypes */- top_parentrc->allMarkTypes |= childrc->allMarkTypes;-- root->rowMarks = lappend(root->rowMarks, childrc);- }-- /*- * Add the RelOptInfo. Even though we may not really scan this relation- * for reasons such as contradictory quals, we still need need to create- * one, because for every RTE in the query's range table, there must be an- * accompanying RelOptInfo.- */-- /* First, store the RTE and appinfos into planner arrays. */- 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;-- childrelopt = build_append_child_rel(root, parentrel, childRTindex);- Assert(childrelopt != NULL);-- return childrelopt;-}--/*- * make_inh_translation_list- * Build the list of translations from parent Vars to child Vars for- * an inheritance child.- *- * For paranoia's sake, we match type/collation as well as attribute name.- */-static void-make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc,- Oid from_rel, Oid to_rel,- Index newvarno, List **translated_vars)-{- List *vars = NIL;- int oldnatts = old_tupdesc->natts;- int newnatts = new_tupdesc->natts;- int old_attno;- int new_attno = 0;-- for (old_attno = 0; old_attno < oldnatts; old_attno++)- {- Form_pg_attribute att;- char *attname;- Oid atttypid;- int32 atttypmod;- Oid attcollation;-- att = TupleDescAttr(old_tupdesc, old_attno);- if (att->attisdropped)- {- /* Just put NULL into this list entry */- vars = lappend(vars, NULL);- continue;- }- attname = NameStr(att->attname);- atttypid = att->atttypid;- atttypmod = att->atttypmod;- attcollation = att->attcollation;-- /*- * When we are generating the "translation list" for the parent table- * of an inheritance set, no need to search for matches.- */- if (from_rel == to_rel)- {- vars = lappend(vars, makeVar(newvarno,- (AttrNumber) (old_attno + 1),- atttypid,- atttypmod,- attcollation,- 0));- continue;- }-- /*- * Otherwise we have to search for the matching column by name.- * There's no guarantee it'll have the same column position, because- * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.- * However, in simple cases, the relative order of columns is mostly- * the same in both relations, so try the column of newrelation that- * follows immediately after the one that we just found, and if that- * fails, let syscache handle it.- */- if (new_attno >= newnatts ||- (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped ||- strcmp(attname, NameStr(att->attname)) != 0)- {- HeapTuple newtup;-- newtup = SearchSysCacheAttName(to_rel, attname);- if (!newtup)- elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",- attname, get_rel_name(to_rel));- new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;- ReleaseSysCache(newtup);-- att = TupleDescAttr(new_tupdesc, new_attno);- }-- /* Found it, check type and collation match */- if (atttypid != att->atttypid || atttypmod != att->atttypmod)- elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",- attname, get_rel_name(to_rel));- if (attcollation != att->attcollation)- elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",- attname, get_rel_name(to_rel));-- vars = lappend(vars, makeVar(newvarno,- (AttrNumber) (new_attno + 1),- atttypid,- atttypmod,- attcollation,- 0));- new_attno++;- }-- *translated_vars = vars;-}--/*- * translate_col_privs- * Translate a bitmapset representing per-column privileges from the- * parent rel's attribute numbering to the child's.- *- * The only surprise here is that we don't translate a parent whole-row- * reference into a child whole-row reference. That would mean requiring- * permissions on all child columns, which is overly strict, since the- * query is really only going to reference the inherited columns. Instead- * we set the per-column bits for all inherited columns.- */-static Bitmapset *-translate_col_privs(const Bitmapset *parent_privs,- List *translated_vars)-{- Bitmapset *child_privs = NULL;- bool whole_row;- int attno;- ListCell *lc;-- /* System attributes have the same numbers in all tables */- for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)- {- if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,- parent_privs))- child_privs = bms_add_member(child_privs,- attno - FirstLowInvalidHeapAttributeNumber);- }-- /* Check if parent has whole-row reference */- whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,- parent_privs);-- /* And now translate the regular user attributes, using the vars list */- attno = InvalidAttrNumber;- foreach(lc, translated_vars)- {- Var *var = lfirst_node(Var, lc);-- attno++;- if (var == NULL) /* ignore dropped columns */- continue;- if (whole_row ||- bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,- parent_privs))- child_privs = bms_add_member(child_privs,- var->varattno - FirstLowInvalidHeapAttributeNumber);- }-- return child_privs;-}--/*- * build_append_child_rel- * Build a RelOptInfo for child relation of an append rel- *- * After creating the RelOptInfo for the given child RT index, it goes on to- * initialize some of its fields base on the parent RelOptInfo.- *- * If the quals in baserestrictinfo turns out to be self-contradictory, the- * RelOptInfo is marked dummy before returning.- */-static RelOptInfo *-build_append_child_rel(PlannerInfo *root,- RelOptInfo *parent,- Index childRTindex)-{- RelOptInfo *childrel;- RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];- AppendRelInfo *appinfo = root->append_rel_array[childRTindex];- List *childquals;- ListCell *lc;- bool have_const_false_cq;- Index cq_min_security;-- /* Build the RelOptInfo. */- childrel = build_simple_rel(root, childRTindex, parent);-- /*- * Propagate lateral_relids and lateral_referencers from appendrel- * parent rels to their child rels. We intentionally give each child rel- * the same minimum parameterization, even though it's quite possible that- * some don't reference all the lateral rels. This is because any append- * path for the parent will have to have the same parameterization for- * every child anyway, and there's no value in forcing extra- * reparameterize_path() calls. Similarly, a lateral reference to the- * parent prevents use of otherwise-movable join rels for each child.- */- childrel->direct_lateral_relids = parent->direct_lateral_relids;- childrel->lateral_relids = parent->lateral_relids;- childrel->lateral_referencers = parent->lateral_referencers;-- /*- * We have to copy the parent's quals to the child, with appropriate- * substitution of variables. However, only the baserestrictinfo- * quals are needed before we can check for constraint exclusion; so- * do that first and then check to see if we can disregard this child.- *- * The child rel's targetlist might contain non-Var expressions, which- * means that substitution into the quals could produce opportunities- * for const-simplification, and perhaps even pseudoconstant quals.- * Therefore, transform each RestrictInfo separately to see if it- * reduces to a constant or pseudoconstant. (We must process them- * separately to keep track of the security level of each qual.)- */- childquals = false;- cq_min_security = UINT_MAX;- have_const_false_cq = false;- foreach(lc, parent->baserestrictinfo)- {- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);- Node *childqual = (Node *) rinfo->clause;- ListCell *lc2;-- Assert(IsA(rinfo, RestrictInfo));- childqual = adjust_appendrel_attrs(root, childqual,- 1, &appinfo);- childqual = eval_const_expressions(root, childqual);- /* check for flat-out constant */- if (childqual && IsA(childqual, Const))- {- if (((Const *) childqual)->constisnull ||- !DatumGetBool(((Const *) childqual)->constvalue))- {- /* Restriction reduces to constant FALSE or NULL */- have_const_false_cq = true;- break;- }- /* Restriction reduces to constant TRUE, so drop it */- continue;- }- /* might have gotten an AND clause, if so flatten it */- foreach(lc2, make_ands_implicit((Expr *) childqual))- {- Node *onecq = (Node *) lfirst(lc2);- bool pseudoconstant;-- /* check for pseudoconstant (no Vars or volatile functions) */- pseudoconstant =- !contain_vars_of_level(onecq, 0) &&- !contain_volatile_functions(onecq);- if (pseudoconstant)- {- /* tell createplan.c to check for gating quals */- root->hasPseudoConstantQuals = true;- }- /* reconstitute RestrictInfo with appropriate properties */- childquals = lappend(childquals,- make_restrictinfo((Expr *) onecq,- rinfo->is_pushed_down,- rinfo->outerjoin_delayed,- pseudoconstant,- rinfo->security_level,- NULL, NULL, NULL));- cq_min_security = Min(cq_min_security, rinfo->security_level);- }- }-- /*- * In addition to the quals inherited from the parent, we might- * have securityQuals associated with this particular child node.- * (Currently this can only happen in appendrels originating from- * UNION ALL; inheritance child tables don't have their own- * securityQuals.) Pull any such securityQuals up into the- * baserestrictinfo for the child. This is similar to- * process_security_barrier_quals() for the parent rel, except- * that we can't make any general deductions from such quals,- * since they don't hold for the whole appendrel.- */- if (childRTE->securityQuals)- {- Index security_level = 0;-- foreach(lc, childRTE->securityQuals)- {- List *qualset = (List *) lfirst(lc);- ListCell *lc2;-- foreach(lc2, qualset)- {- Expr *qual = (Expr *) lfirst(lc2);-- /*- * not likely that we'd see constants here, so no- * check- */- childquals = lappend(childquals,- make_restrictinfo(qual,- true,- false,- false,- security_level,- NULL, NULL,- NULL));- cq_min_security = Min(cq_min_security, security_level);- }- security_level++;- }- Assert(security_level <= root->qual_security_level);- }-- /* Set child's version of baserestrictinfo. */- childrel->baserestrictinfo = childquals;- childrel->baserestrict_min_security = cq_min_security;-- if (have_const_false_cq)- {- /*- * Some restriction clause reduced to constant FALSE or NULL after- * substitution, so this child need not be scanned.- */- set_dummy_rel_pathlist(childrel);- }-- return childrel;-}--/*- * add_rowmark_junk_columns- * Add necessary junk columns for rowmarked inheritance parent rel.- *- * These values are needed for locking of rels selected FOR UPDATE/SHARE, and- * to do EvalPlanQual rechecking. See comments for PlanRowMark in- * plannodes.h.- */-static List *-add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc)-{- List *tlist = root->processed_tlist;- Var *var;- char resname[32];- TargetEntry *tle;-- if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))- {- /* Need to fetch TID */- var = makeVar(rc->rti,- SelfItemPointerAttributeNumber,- TIDOID,- -1,- InvalidOid,- 0);- snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);- tle = makeTargetEntry((Expr *) var,- list_length(tlist) + 1,- pstrdup(resname),- true);- tlist = lappend(tlist, tle);- }- if (rc->allMarkTypes & (1 << ROW_MARK_COPY))- {- /* Need the whole row as a junk var */- var = makeWholeRowVar(root->simple_rte_array[rc->rti],- rc->rti,- 0,- false);- snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);- tle = makeTargetEntry((Expr *) var,- list_length(tlist) + 1,- pstrdup(resname),- true);- tlist = lappend(tlist, tle);- }-- /* For inheritance cases, always fetch the tableoid too. */- var = makeVar(rc->rti,- TableOidAttributeNumber,- OIDOID,- -1,- InvalidOid,- 0);- snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);- tle = makeTargetEntry((Expr *) var,- list_length(tlist) + 1,- pstrdup(resname),- true);- tlist = lappend(tlist, tle);-- return tlist;-}--AppendRelInfo *-make_append_rel_info(RelOptInfo *parentrel, RangeTblEntry *parentrte,- TupleDesc childdesc, Oid childoid, Oid childreltype,- Index childRTindex)-{- AppendRelInfo *appinfo = makeNode(AppendRelInfo);-- appinfo->parent_relid = parentrel->relid;- appinfo->child_relid = childRTindex;- appinfo->parent_reltype = parentrel->reltype;- appinfo->child_reltype = childreltype;- make_inh_translation_list(parentrel->tupdesc, childdesc,- parentrte->relid, childoid,- childRTindex,- &appinfo->translated_vars);- appinfo->parent_reloid = parentrte->relid;-- return appinfo;-}--/*- * adjust_appendrel_attrs- * Copy the specified query or expression and translate Vars referring to a- * parent rel to refer to the corresponding child rel instead. We also- * update rtindexes appearing outside Vars, such as resultRelation and- * jointree relids.- *- * Note: this is only applied after conversion of sublinks to subplans,- * so we don't need to cope with recursion into sub-queries.- *- * Note: this is not hugely different from what pullup_replace_vars() does;- * maybe we should try to fold the two routines together.- */-Node *-adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,- AppendRelInfo **appinfos)-{- Node *result;- adjust_appendrel_attrs_context context;-- context.root = root;- context.nappinfos = nappinfos;- context.appinfos = appinfos;-- /* If there's nothing to adjust, don't call this function. */- Assert(nappinfos >= 1 && appinfos != NULL);-- /*- * Must be prepared to start with a Query or a bare expression tree.- */- if (node && IsA(node, Query))- {- Query *newnode;- int cnt;-- newnode = query_tree_mutator((Query *) node,- adjust_appendrel_attrs_mutator,- (void *) &context,- QTW_IGNORE_RC_SUBQUERIES);- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- if (newnode->resultRelation == appinfo->parent_relid)- {- newnode->resultRelation = appinfo->child_relid;- /* Fix tlist resnos too, if it's inherited UPDATE */- if (newnode->commandType == CMD_UPDATE)- newnode->targetList =- adjust_inherited_tlist(newnode->targetList,- appinfo);- break;- }- }-- result = (Node *) newnode;- }- else- result = adjust_appendrel_attrs_mutator(node, &context);-- return result;-}--static Node *-adjust_appendrel_attrs_mutator(Node *node,- adjust_appendrel_attrs_context *context)-{- AppendRelInfo **appinfos = context->appinfos;- int nappinfos = context->nappinfos;- int cnt;-- if (node == NULL)- return NULL;- if (IsA(node, Var))- {- Var *var = (Var *) copyObject(node);- AppendRelInfo *appinfo = NULL;-- for (cnt = 0; cnt < nappinfos; cnt++)- {- if (var->varno == appinfos[cnt]->parent_relid)- {- appinfo = appinfos[cnt];- break;- }- }-- if (var->varlevelsup == 0 && appinfo)- {- var->varno = appinfo->child_relid;- var->varnoold = appinfo->child_relid;- if (var->varattno > 0)- {- Node *newnode;-- if (var->varattno > list_length(appinfo->translated_vars))- elog(ERROR, "attribute %d of relation \"%s\" does not exist",- var->varattno, get_rel_name(appinfo->parent_reloid));- newnode = copyObject(list_nth(appinfo->translated_vars,- var->varattno - 1));- if (newnode == NULL)- elog(ERROR, "attribute %d of relation \"%s\" does not exist",- var->varattno, get_rel_name(appinfo->parent_reloid));- return newnode;- }- else if (var->varattno == 0)- {- /*- * Whole-row Var: if we are dealing with named rowtypes, we- * can use a whole-row Var for the child table plus a coercion- * step to convert the tuple layout to the parent's rowtype.- * Otherwise we have to generate a RowExpr.- */- if (OidIsValid(appinfo->child_reltype))- {- Assert(var->vartype == appinfo->parent_reltype);- if (appinfo->parent_reltype != appinfo->child_reltype)- {- ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);-- r->arg = (Expr *) var;- r->resulttype = appinfo->parent_reltype;- r->convertformat = COERCE_IMPLICIT_CAST;- r->location = -1;- /* Make sure the Var node has the right type ID, too */- var->vartype = appinfo->child_reltype;- return (Node *) r;- }- }- else- {- /*- * Build a RowExpr containing the translated variables.- *- * In practice var->vartype will always be RECORDOID here,- * so we need to come up with some suitable column names.- * We use the parent RTE's column names.- *- * Note: we can't get here for inheritance cases, so there- * is no need to worry that translated_vars might contain- * some dummy NULLs.- */- RowExpr *rowexpr;- List *fields;- RangeTblEntry *rte;-- rte = rt_fetch(appinfo->parent_relid,- context->root->parse->rtable);- fields = copyObject(appinfo->translated_vars);- rowexpr = makeNode(RowExpr);- rowexpr->args = fields;- rowexpr->row_typeid = var->vartype;- rowexpr->row_format = COERCE_IMPLICIT_CAST;- rowexpr->colnames = copyObject(rte->eref->colnames);- rowexpr->location = -1;-- return (Node *) rowexpr;- }- }- /* system attributes don't need any other translation */- }- return (Node *) var;- }- if (IsA(node, CurrentOfExpr))- {- CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);-- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- if (cexpr->cvarno == appinfo->parent_relid)- {- cexpr->cvarno = appinfo->child_relid;- break;- }- }- return (Node *) cexpr;- }- if (IsA(node, RangeTblRef))- {- RangeTblRef *rtr = (RangeTblRef *) copyObject(node);-- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- if (rtr->rtindex == appinfo->parent_relid)- {- rtr->rtindex = appinfo->child_relid;- break;- }- }- return (Node *) rtr;- }- if (IsA(node, JoinExpr))- {- /* Copy the JoinExpr node with correct mutation of subnodes */- JoinExpr *j;- AppendRelInfo *appinfo;-- j = (JoinExpr *) expression_tree_mutator(node,- adjust_appendrel_attrs_mutator,- (void *) context);- /* now fix JoinExpr's rtindex (probably never happens) */- for (cnt = 0; cnt < nappinfos; cnt++)- {- appinfo = appinfos[cnt];-- if (j->rtindex == appinfo->parent_relid)- {- j->rtindex = appinfo->child_relid;- break;- }- }- return (Node *) j;- }- if (IsA(node, PlaceHolderVar))- {- /* Copy the PlaceHolderVar node with correct mutation of subnodes */- PlaceHolderVar *phv;-- phv = (PlaceHolderVar *) expression_tree_mutator(node,- adjust_appendrel_attrs_mutator,- (void *) context);- /* now fix PlaceHolderVar's relid sets */- if (phv->phlevelsup == 0)- phv->phrels = adjust_child_relids(phv->phrels, context->nappinfos,- context->appinfos);- return (Node *) phv;- }-- /*- * This is needed, because inheritance_make_rel_from_joinlist needs to- * translate root->join_info_list executing make_rel_from_joinlist for a- * given child.- */- if (IsA(node, SpecialJoinInfo))- {- SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node;- SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo);-- memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo));- newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand,- context->nappinfos,- context->appinfos);- newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand,- context->nappinfos,- context->appinfos);- newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand,- context->nappinfos,- context->appinfos);- newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand,- context->nappinfos,- context->appinfos);- newinfo->semi_rhs_exprs =- (List *) expression_tree_mutator((Node *)- oldinfo->semi_rhs_exprs,- adjust_appendrel_attrs_mutator,- (void *) context);- return (Node *) newinfo;- }-- /* Shouldn't need to handle planner auxiliary nodes here */- Assert(!IsA(node, AppendRelInfo));- Assert(!IsA(node, PlaceHolderInfo));- Assert(!IsA(node, MinMaxAggInfo));-- /*- * We have to process RestrictInfo nodes specially. (Note: although- * set_append_rel_pathlist will hide RestrictInfos in the parent's- * baserestrictinfo list from us, it doesn't hide those in joininfo.)- */- if (IsA(node, RestrictInfo))- {- RestrictInfo *oldinfo = (RestrictInfo *) node;- RestrictInfo *newinfo = makeNode(RestrictInfo);-- /* Copy all flat-copiable fields */- memcpy(newinfo, oldinfo, sizeof(RestrictInfo));-- /* Recursively fix the clause itself */- newinfo->clause = (Expr *)- adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);-- /* and the modified version, if an OR clause */- newinfo->orclause = (Expr *)- adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);-- /* adjust relid sets too */- newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,- context->nappinfos,- context->appinfos);- newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,- context->nappinfos,- context->appinfos);- newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,- context->nappinfos,- context->appinfos);- newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids,- context->nappinfos,- context->appinfos);- newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,- context->nappinfos,- context->appinfos);- newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,- context->nappinfos,- context->appinfos);-- /*- * Reset cached derivative fields, since these might need to have- * different values when considering the child relation. Note we- * don't reset left_ec/right_ec: each child variable is implicitly- * equivalent to its parent, so still a member of the same EC if any.- */- newinfo->eval_cost.startup = -1;- newinfo->norm_selec = -1;- newinfo->outer_selec = -1;- newinfo->left_em = NULL;- newinfo->right_em = NULL;- newinfo->scansel_cache = NIL;- newinfo->left_bucketsize = -1;- newinfo->right_bucketsize = -1;- newinfo->left_mcvfreq = -1;- newinfo->right_mcvfreq = -1;-- return (Node *) newinfo;- }-- /*- * NOTE: we do not need to recurse into sublinks, because they should- * already have been converted to subplans before we see them.- */- Assert(!IsA(node, SubLink));- Assert(!IsA(node, Query));-- return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,- (void *) context);-}--/*- * Substitute child relids for parent relids in a Relid set. The array of- * appinfos specifies the substitutions to be performed.- */-static Relids-adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)-{- Bitmapset *result = NULL;- int cnt;-- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- /* Remove parent, add child */- if (bms_is_member(appinfo->parent_relid, relids))- {- /* Make a copy if we are changing the set. */- if (!result)- result = bms_copy(relids);-- result = bms_del_member(result, appinfo->parent_relid);- result = bms_add_member(result, appinfo->child_relid);- }- }-- /* If we made any changes, return the modified copy. */- if (result)- return result;-- /* Otherwise, return the original set without modification. */- return relids;-}--/*- * Replace any relid present in top_parent_relids with its child in- * child_relids. Members of child_relids can be multiple levels below top- * parent in the partition hierarchy.- */-Relids-adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,- Relids child_relids, Relids top_parent_relids)-{- AppendRelInfo **appinfos;- int nappinfos;- Relids parent_relids = NULL;- Relids result;- Relids tmp_result = NULL;- int cnt;-- /*- * If the given relids set doesn't contain any of the top parent relids,- * it will remain unchanged.- */- if (!bms_overlap(relids, top_parent_relids))- return relids;-- appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);-- /* Construct relids set for the immediate parent of the given child. */- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);- }-- /* Recurse if immediate parent is not the top parent. */- if (!bms_equal(parent_relids, top_parent_relids))- {- tmp_result = adjust_child_relids_multilevel(root, relids,- parent_relids,- top_parent_relids);- relids = tmp_result;- }-- result = adjust_child_relids(relids, nappinfos, appinfos);-- /* Free memory consumed by any intermediate result. */- if (tmp_result)- bms_free(tmp_result);- bms_free(parent_relids);- pfree(appinfos);-- return result;-}--/*- * Adjust the targetlist entries of an inherited UPDATE operation- *- * The expressions have already been fixed, but we have to make sure that- * the target resnos match the child table (they may not, in the case of- * a column that was added after-the-fact by ALTER TABLE). In some cases- * this can force us to re-order the tlist to preserve resno ordering.- * (We do all this work in special cases so that preptlist.c is fast for- * the typical case.)- *- * The given tlist has already been through expression_tree_mutator;- * therefore the TargetEntry nodes are fresh copies that it's okay to- * scribble on.- *- * Note that this is not needed for INSERT because INSERT isn't inheritable.- */-static List *-adjust_inherited_tlist(List *tlist, AppendRelInfo *context)-{- bool changed_it = false;- ListCell *tl;- List *new_tlist;- bool more;- int attrno;-- /* This should only happen for an inheritance case, not UNION ALL */- Assert(OidIsValid(context->parent_reloid));-- /* Scan tlist and update resnos to match attnums of child rel */- foreach(tl, tlist)- {- TargetEntry *tle = (TargetEntry *) lfirst(tl);- Var *childvar;-- if (tle->resjunk)- continue; /* ignore junk items */-- /* Look up the translation of this column: it must be a Var */- if (tle->resno <= 0 ||- tle->resno > list_length(context->translated_vars))- elog(ERROR, "attribute %d of relation \"%s\" does not exist",- tle->resno, get_rel_name(context->parent_reloid));- childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);- if (childvar == NULL || !IsA(childvar, Var))- elog(ERROR, "attribute %d of relation \"%s\" does not exist",- tle->resno, get_rel_name(context->parent_reloid));-- if (tle->resno != childvar->varattno)- {- tle->resno = childvar->varattno;- changed_it = true;- }- }-- /*- * If we changed anything, re-sort the tlist by resno, and make sure- * resjunk entries have resnos above the last real resno. The sort- * algorithm is a bit stupid, but for such a seldom-taken path, small is- * probably better than fast.- */- if (!changed_it)- return tlist;-- new_tlist = NIL;- more = true;- for (attrno = 1; more; attrno++)- {- more = false;- foreach(tl, tlist)- {- TargetEntry *tle = (TargetEntry *) lfirst(tl);-- if (tle->resjunk)- continue; /* ignore junk items */-- if (tle->resno == attrno)- new_tlist = lappend(new_tlist, tle);- else if (tle->resno > attrno)- more = true;- }- }-- foreach(tl, tlist)- {- TargetEntry *tle = (TargetEntry *) lfirst(tl);-- if (!tle->resjunk)- continue; /* here, ignore non-junk items */-- tle->resno = attrno;- new_tlist = lappend(new_tlist, tle);- attrno++;- }-- return new_tlist;-}--/*- * adjust_appendrel_attrs_multilevel- * Apply Var translations from a toplevel appendrel parent down to a child.- *- * In some cases we need to translate expressions referencing a parent relation- * to reference an appendrel child that's multiple levels removed from it.- */-Node *-adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,- Relids child_relids,- Relids top_parent_relids)-{- AppendRelInfo **appinfos;- Bitmapset *parent_relids = NULL;- int nappinfos;- int cnt;-- Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids));-- appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);-- /* Construct relids set for the immediate parent of given child. */- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);- }-- /* Recurse if immediate parent is not the top parent. */- if (!bms_equal(parent_relids, top_parent_relids))- node = adjust_appendrel_attrs_multilevel(root, node, parent_relids,- top_parent_relids);-- /* Now translate for this child */- node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);-- pfree(appinfos);-- return node;-}--/*- * Construct the SpecialJoinInfo for a child-join by translating- * SpecialJoinInfo for the join between parents. left_relids and right_relids- * are the relids of left and right side of the join respectively.- */-SpecialJoinInfo *-build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,- Relids left_relids, Relids right_relids)-{- SpecialJoinInfo *sjinfo = makeNode(SpecialJoinInfo);- AppendRelInfo **left_appinfos;- int left_nappinfos;- AppendRelInfo **right_appinfos;- int right_nappinfos;-- memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));- left_appinfos = find_appinfos_by_relids(root, left_relids,- &left_nappinfos);- right_appinfos = find_appinfos_by_relids(root, right_relids,- &right_nappinfos);-- sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand,- left_nappinfos, left_appinfos);- sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand,- right_nappinfos,- right_appinfos);- sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand,- left_nappinfos, left_appinfos);- sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand,- right_nappinfos,- right_appinfos);- sjinfo->semi_rhs_exprs = (List *) adjust_appendrel_attrs(root,- (Node *) sjinfo->semi_rhs_exprs,- right_nappinfos,- right_appinfos);-- pfree(left_appinfos);- pfree(right_appinfos);-- return sjinfo;-}--/*- * find_appinfos_by_relids- * Find AppendRelInfo structures for all relations specified by relids.- *- * The AppendRelInfos are returned in an array, which can be pfree'd by the- * caller. *nappinfos is set to the number of entries in the array.- */-AppendRelInfo **-find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)-{- AppendRelInfo **appinfos;- int cnt = 0;- int i;-- *nappinfos = bms_num_members(relids);- appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos);-- i = -1;- while ((i = bms_next_member(relids, i)) >= 0)- {- AppendRelInfo *appinfo = root->append_rel_array[i];-- if (!appinfo)- elog(ERROR, "child rel %d not found in append_rel_array", i);-- appinfos[cnt++] = appinfo;- }- return appinfos;-}diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefileindex da9ccf32b4..d166030a10 100644--- a/src/backend/optimizer/util/Makefile+++ b/src/backend/optimizer/util/Makefile@@ -12,7 +12,8 @@ subdir = src/backend/optimizer/util
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = clauses.o joininfo.o orclauses.o pathnode.o placeholder.o \- plancat.o predtest.o relnode.o restrictinfo.o tlist.o var.o+OBJS = append.o appendinfo.o clauses.o joininfo.o orclauses.o pathnode.o \+ placeholder.o plancat.o predtest.o relnode.o restrictinfo.o tlist.o \+ var.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/optimizer/util/append.c b/src/backend/optimizer/util/append.cnew file mode 100644index 0000000000..bbc579817a--- /dev/null+++ b/src/backend/optimizer/util/append.c@@ -0,0 +1,719 @@+/*-------------------------------------------------------------------------+ *+ * append.c+ * Routines to process children of an appendrel parent+ *+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ *+ * IDENTIFICATION+ * src/backend/optimizer/path/append.c+ *+ *-------------------------------------------------------------------------+ */+#include "postgres.h"++#include <limits.h>++#include "access/sysattr.h"+#include "catalog/partition.h"+#include "catalog/pg_class.h"+#include "catalog/pg_inherits.h"+#include "catalog/pg_type.h"+#include "nodes/makefuncs.h"+#include "optimizer/append.h"+#include "optimizer/appendinfo.h"+#include "optimizer/clauses.h"+#include "optimizer/pathnode.h"+#include "optimizer/paths.h"+#include "optimizer/plancat.h"+#include "optimizer/planmain.h"+#include "optimizer/planner.h"+#include "optimizer/prep.h"+#include "optimizer/restrictinfo.h"+#include "optimizer/tlist.h"+#include "optimizer/var.h"+#include "partitioning/partprune.h"+#include "utils/rel.h"++static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,+ Index rti, RelOptInfo *rel);+static void expand_partitioned_rtentry(PlannerInfo *root,+ RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel);+static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root,+ RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel,+ PlanRowMark *top_parentrc, Relation childrel);+static RelOptInfo *build_append_child_rel(PlannerInfo *root,+ RelOptInfo *parent,+ Index childRTindex);+static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc);+++/*+ * expand_append_rel+ * This initializes RelOptInfos for an appendrel's child relations.+ *+ * 'rel' is the appendrel parent, whose range table entry ('rte') has been+ * marked to require adding children. An appendrel parent could either+ * be a subquery (if we flattened UNION ALL query) or a table that's known+ * to have inheritance children. The latter consists of both regular+ * inheritance parents and partitioned tables.+ *+ * For a subquery parent, there is not much to be done here because the+ * children's RTEs are already present in the query, so we just initialize+ * RelOptInfos for them. Also, the AppendRelInfos for child subqueries+ * have already been added.+ *+ * For tables, we need to add the children to the range table and initialize+ * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them. For+ * a partitioned parent, we only add the children remaining after pruning.+ * For regular inheritance parents, we find the children using+ * find_all_inheritors and add all of them.+ *+ * If it turns out that there are no children, then we set rte->inh to false+ * to let the caller know that only the parent table needs to be scanned. The+ * caller can accordingly switch to a non-Append path. For a partitioned+ * parent, that means an empty relation because parents themselves contain no+ * data.+ *+ * For the regular inheritance case, the parent also gets another RTE with+ * inh = false to represent it as an appendrel child. The original RTE is+ * considered to represent the whole inheritance set.+ */+void+expand_append_rel(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,+ Index rti)+{+ Assert(rte->inh);+ /* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */+ Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY);++ /*+ * UNION ALL children already got RTEs and AppendRelInfos, so just build+ * RelOptInfos and return.+ *+ * It might be a bit odd that this code is in this, because there is+ * nothing to expand really.+ */+ if (rte->rtekind == RTE_SUBQUERY)+ {+ ListCell *l;++ /*+ * We don't need to use expand_planner_arrays in this case, because+ * no new child RTEs are created. setup_simple_rel_arrays() and+ * setup_append_rel_array would've considered these child RTEs when+ * allocating space for various arrays.+ */+ foreach(l, root->append_rel_list)+ {+ AppendRelInfo *appinfo = lfirst(l);+ Index childRTindex = appinfo->child_relid;++ if (appinfo->parent_relid != rti)+ continue;++ Assert(childRTindex < root->simple_rel_array_size);+ Assert(root->simple_rte_array[childRTindex] != NULL);++ /*+ * We set the correct value of baserestricinfo and+ * baserestrict_min_security below.+ */+ root->simple_rel_array[childRTindex] =+ build_append_child_rel(root, rel, appinfo->child_relid);+ }+ }+ else+ {+ Assert(rte->rtekind == RTE_RELATION);+ Assert(has_subclass(rte->relid));++ /*+ * The rewriter should already have obtained an appropriate lock on+ * each relation named in the query. However, for each child relation+ * we add to the query, we must obtain an appropriate lock, because+ * this will be the first use of those relations in the+ * parse/rewrite/plan pipeline. Child rels should use the same+ * lockmode as their parent.+ */+ Assert(rte->rellockmode != NoLock);++ if (rte->relkind == RELKIND_PARTITIONED_TABLE)+ expand_partitioned_rtentry(root, rte, rti, rel);+ else+ expand_inherited_rtentry(root, rte, rti, rel);+ }+}++/*+ * expand_inherited_rtentry+ * Add entries for all the child tables to the query's rangetable, and+ * build AppendRelInfo nodes for all the child tables and add them to+ * root->append_rel_list.+ *+ * Note that the original RTE is considered to represent the whole+ * inheritance set. The first of the generated RTEs is an RTE for the same+ * table, but with inh = false, to represent the parent table in its role+ * as a simple member of the inheritance set.+ *+ * A childless table is never considered to be an inheritance set. For+ * regular inheritance, a parent RTE must always have at least two associated+ * AppendRelInfos: one corresponding to the parent table as a simple member of+ * inheritance set and one or more corresponding to the actual children.+ */+static void+expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti,+ RelOptInfo *rel)+{+ Oid parentOID;+ PlanRowMark *oldrc;+ LOCKMODE lockmode = rte->rellockmode;+ List *inhOIDs;+ ListCell *l;+ int num_children;+ int num_children_added = 0;++ Assert(rte->rtekind == RTE_RELATION);+ Assert(lockmode != NoLock);+ parentOID = rte->relid;++ /* Scan for all members of inheritance set, acquire needed locks */+ inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);++ /*+ * Check that there's at least one descendant, else treat as no-child+ * case. This could happen despite above has_subclass() check, if table+ * once had a child but no longer does.+ */+ num_children = list_length(inhOIDs);+ if (num_children < 2)+ {+ /* Clear flag before returning */+ rte->inh = false;+ return;+ }++ /*+ * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks+ * should've set isParent = true. We'll generate a new PlanRowMark for+ * each child.+ */+ oldrc = get_plan_rowmark(root->rowMarks, rti);+ Assert(oldrc == NULL || oldrc->isParent);++ /*+ * Must expand PlannerInfo arrays by num_children before we can add+ * children.+ */+ expand_planner_arrays(root, num_children);++ foreach(l, inhOIDs)+ {+ Oid childOID = lfirst_oid(l);+ Relation newrelation;+ RelOptInfo *childrel;++ /* Already locked above. */+ newrelation = heap_open(childOID, NoLock);++ /*+ * It is possible that the parent table has children that are temp+ * tables of other backends. We cannot safely access such tables+ * (because of buffering issues), and the best thing to do seems+ * to be to silently ignore them.+ */+ if (RELATION_IS_OTHER_TEMP(newrelation))+ {+ heap_close(newrelation, lockmode);+ continue;+ }++ childrel = add_inheritance_child_rel(root, rte, rti, rel, oldrc,+ newrelation);+ Assert(childrel != NULL);++ /* Close child relations, but keep locks */+ heap_close(newrelation, NoLock);+ num_children_added++;+ }++ /*+ * If all children, including the parent (as child rel), were+ * excluded, mark the parent rel as empty. If all the children were temp+ * tables, pretend it's a non-inheritance situation; we don't need Append+ * node in that case. The duplicate RTE we added for the parent table is+ * harmless, so we don't bother to get rid of it; ditto for the useless+ * PlanRowMark node.+ */+ if (num_children_added == 0)+ mark_dummy_rel(rel);+ else if (num_children_added == 1)+ rte->inh = false;++ /*+ * Add junk columns needed by the row mark if any and also add the+ * relevant expressions to the root parent's reltarget.+ */+ if (oldrc)+ {+ List *tlist = add_rowmark_junk_columns(root, oldrc);++ build_base_rel_tlists(root, tlist);+ }+}++/*+ * expand_partitioned_rtentry+ * Prunes unnecessary partitions of a partitioned table and adds+ * remaining ones to the Query and the PlannerInfo+ *+ * Partitions are added to the query in order in which they are found in+ * the parent's PartitionDesc.+ *+ * Note: even though only the unpruned partitions will be added to the+ * resulting plan, this still locks *all* partitions via find_all_inheritors+ * in order to avoid partitions being locked in a different order than other+ * places in the backend that may lock partitions.+ */+static void+expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel)+{+ LOCKMODE lockmode = parentrte->rellockmode;+ PlanRowMark *rootrc = NULL;+ int i;+ Bitmapset *partindexes;+ Index rootParentRTindex = parentrel->inh_root_parent > 0 ?+ parentrel->inh_root_parent :+ parentRTindex;++ /* If root partitioned table, lock *all* partitions in the tree. */+ if (parentRTindex == rootParentRTindex)+ (void) find_all_inheritors(parentrte->relid, lockmode, NULL);++ /*+ * Initialize partitioned_child_rels to contain this RT index.+ *+ * Note that during the set_append_rel_pathlist() phase, values of the+ * indexes of partitioned relations that appear down in the tree will be+ * bubbled up into root parent's list so that when we've created Paths for+ * all the children, the root table's list will contain all such indexes.+ */+ parentrel->partitioned_child_rels = list_make1_int(parentRTindex);++ /* Perform pruning. */+ partindexes = prune_append_rel_partitions(parentrel);++ /* Must expand PlannerInfo arrays before we can add children. */+ expand_planner_arrays(root, bms_num_members(partindexes));++ /*+ * For partitioned tables, we also store the partition RelOptInfo+ * pointers in the parent's RelOptInfo.+ */+ parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *+ parentrel->nparts);++ rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex);+ Assert(rootrc == NULL || rootrc->isParent);+ i = -1;+ while ((i = bms_next_member(partindexes, i)) >= 0)+ {+ Oid childOID = parentrel->part_oids[i];+ Relation newrelation;+ RelOptInfo *childrel;++ /* Already locked above. */+ newrelation = heap_open(childOID, NoLock);+ Assert(!RELATION_IS_OTHER_TEMP(newrelation));++ /*+ * A partitioned child table with 0 children is a dummy rel, so don't+ * bother creating planner objects for it.+ */+ if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&+ RelationGetPartitionDesc(newrelation)->nparts == 0)+ {+ heap_close(newrelation, NoLock);+ continue;+ }++ childrel = add_inheritance_child_rel(root, parentrte, parentRTindex,+ parentrel, rootrc, newrelation);+ Assert(childrel != NULL);+ parentrel->part_rels[i] = childrel;++ /* Close child relations, but keep locks */+ heap_close(newrelation, NoLock);+ }++ /*+ * Add junk columns needed by the row mark if any and also add the+ * relevant expressions to the root parent's reltarget.+ */+ if (rootrc)+ {+ List *tlist = add_rowmark_junk_columns(root, rootrc);++ build_base_rel_tlists(root, tlist);+ }+}++/*+ * add_inheritance_child_rel+ * Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally+ * a RelOptInfo for an inheritance child relation.+ *+ * The return value is the RelOptInfo that's added.+ *+ * PlanRowMarks still carry the top-parent's RTI, and the top-parent's+ * allMarkTypes field still accumulates values from all descendents.+ *+ * "parentrte" and "parentRTindex" are immediate parent's RTE and+ * RTI. "top_parentrc" is top parent's PlanRowMark.+ */+static RelOptInfo *+add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel,+ PlanRowMark *top_parentrc, Relation childrel)+{+ Query *parse = root->parse;+ Oid childOID = RelationGetRelid(childrel);+ RangeTblEntry *childrte;+ Index childRTindex;+ AppendRelInfo *appinfo;+ RelOptInfo *childrelopt;++ /*+ * Build an RTE for the child, and attach to query's rangetable list. We+ * copy most fields of the parent's RTE, but replace relation OID and+ * relkind, and set inh appropriately. Also, set requiredPerms to zero+ * since all required permissions checks are done on the original RTE.+ * Likewise, set the child's securityQuals to empty, because we only want+ * to apply the parent's RLS conditions regardless of what RLS properties+ * individual children may have. (This is an intentional choice to make+ * inherited RLS work like regular permissions checks.) The parent+ * securityQuals will be propagated to children along with other base+ * restriction clauses, so we don't need to do it here.+ */+ childrte = copyObject(parentrte);+ childrte->relid = childOID;+ childrte->relkind = childrel->rd_rel->relkind;+ /*+ * A partitioned child will need to be expanded as an append parent+ * itself, so set its inh to true.+ */+ childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE);+ childrte->requiredPerms = 0;+ childrte->securityQuals = NIL;+ parse->rtable = lappend(parse->rtable, childrte);+ childRTindex = list_length(parse->rtable);++ /* Create an AppendRelInfo and add it to planner's global list. */+ appinfo = make_append_rel_info(parentrel, parentrte,+ childrel->rd_att,+ childOID,+ childrel->rd_rel->reltype,+ childRTindex);+ root->append_rel_list = lappend(root->append_rel_list, appinfo);++ /*+ * Translate the column permissions bitmaps to the child's attnums (we+ * have to build the translated_vars list before we can do this). But+ * if this is the parent table, leave copyObject's result alone.+ *+ * Note: we need to do this even though the executor won't run any+ * permissions checks on the child RTE. The insertedCols/updatedCols+ * bitmaps may be examined for trigger-firing purposes.+ */+ if (childrte->relid != parentrte->relid)+ {+ childrte->selectedCols = translate_col_privs(parentrte->selectedCols,+ appinfo->translated_vars);+ childrte->insertedCols = translate_col_privs(parentrte->insertedCols,+ appinfo->translated_vars);+ childrte->updatedCols = translate_col_privs(parentrte->updatedCols,+ appinfo->translated_vars);+ }++ /*+ * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.+ */+ if (top_parentrc)+ {+ PlanRowMark *childrc = makeNode(PlanRowMark);++ childrc->rti = childRTindex;+ childrc->prti = top_parentrc->rti;+ childrc->rowmarkId = top_parentrc->rowmarkId;+ /* Reselect rowmark type, because relkind might not match parent */+ childrc->markType = select_rowmark_type(childrte,+ top_parentrc->strength);+ childrc->allMarkTypes = (1 << childrc->markType);+ childrc->strength = top_parentrc->strength;+ childrc->waitPolicy = top_parentrc->waitPolicy;++ /*+ * We mark RowMarks for partitioned child tables as parent RowMarks so+ * that the executor ignores them (except their existence means that+ * the child tables be locked using appropriate mode).+ */+ childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);++ /* Include child's rowmark type in top parent's allMarkTypes */+ top_parentrc->allMarkTypes |= childrc->allMarkTypes;++ root->rowMarks = lappend(root->rowMarks, childrc);+ }++ /*+ * Add the RelOptInfo. Even though we may not really scan this relation+ * for reasons such as contradictory quals, we still need need to create+ * one, because for every RTE in the query's range table, there must be an+ * accompanying RelOptInfo.+ */++ /* First, store the RTE and appinfos into planner arrays. */+ 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;++ childrelopt = build_append_child_rel(root, parentrel, childRTindex);+ Assert(childrelopt != NULL);++ return childrelopt;+}++/*+ * build_append_child_rel+ * Build a RelOptInfo for child relation of an append rel+ *+ * After creating the RelOptInfo for the given child RT index, it goes on to+ * initialize some of its fields base on the parent RelOptInfo.+ *+ * If the quals in baserestrictinfo turns out to be self-contradictory, the+ * RelOptInfo is marked dummy before returning.+ */+static RelOptInfo *+build_append_child_rel(PlannerInfo *root,+ RelOptInfo *parent,+ Index childRTindex)+{+ RelOptInfo *childrel;+ RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];+ AppendRelInfo *appinfo = root->append_rel_array[childRTindex];+ List *childquals;+ ListCell *lc;+ bool have_const_false_cq;+ Index cq_min_security;++ /* Build the RelOptInfo. */+ childrel = build_simple_rel(root, childRTindex, parent);++ /*+ * Propagate lateral_relids and lateral_referencers from appendrel+ * parent rels to their child rels. We intentionally give each child rel+ * the same minimum parameterization, even though it's quite possible that+ * some don't reference all the lateral rels. This is because any append+ * path for the parent will have to have the same parameterization for+ * every child anyway, and there's no value in forcing extra+ * reparameterize_path() calls. Similarly, a lateral reference to the+ * parent prevents use of otherwise-movable join rels for each child.+ */+ childrel->direct_lateral_relids = parent->direct_lateral_relids;+ childrel->lateral_relids = parent->lateral_relids;+ childrel->lateral_referencers = parent->lateral_referencers;++ /*+ * We have to copy the parent's quals to the child, with appropriate+ * substitution of variables. However, only the baserestrictinfo+ * quals are needed before we can check for constraint exclusion; so+ * do that first and then check to see if we can disregard this child.+ *+ * The child rel's targetlist might contain non-Var expressions, which+ * means that substitution into the quals could produce opportunities+ * for const-simplification, and perhaps even pseudoconstant quals.+ * Therefore, transform each RestrictInfo separately to see if it+ * reduces to a constant or pseudoconstant. (We must process them+ * separately to keep track of the security level of each qual.)+ */+ childquals = false;+ cq_min_security = UINT_MAX;+ have_const_false_cq = false;+ foreach(lc, parent->baserestrictinfo)+ {+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);+ Node *childqual = (Node *) rinfo->clause;+ ListCell *lc2;++ Assert(IsA(rinfo, RestrictInfo));+ childqual = adjust_appendrel_attrs(root, childqual,+ 1, &appinfo);+ childqual = eval_const_expressions(root, childqual);+ /* check for flat-out constant */+ if (childqual && IsA(childqual, Const))+ {+ if (((Const *) childqual)->constisnull ||+ !DatumGetBool(((Const *) childqual)->constvalue))+ {+ /* Restriction reduces to constant FALSE or NULL */+ have_const_false_cq = true;+ break;+ }+ /* Restriction reduces to constant TRUE, so drop it */+ continue;+ }+ /* might have gotten an AND clause, if so flatten it */+ foreach(lc2, make_ands_implicit((Expr *) childqual))+ {+ Node *onecq = (Node *) lfirst(lc2);+ bool pseudoconstant;++ /* check for pseudoconstant (no Vars or volatile functions) */+ pseudoconstant =+ !contain_vars_of_level(onecq, 0) &&+ !contain_volatile_functions(onecq);+ if (pseudoconstant)+ {+ /* tell createplan.c to check for gating quals */+ root->hasPseudoConstantQuals = true;+ }+ /* reconstitute RestrictInfo with appropriate properties */+ childquals = lappend(childquals,+ make_restrictinfo((Expr *) onecq,+ rinfo->is_pushed_down,+ rinfo->outerjoin_delayed,+ pseudoconstant,+ rinfo->security_level,+ NULL, NULL, NULL));+ cq_min_security = Min(cq_min_security, rinfo->security_level);+ }+ }++ /*+ * In addition to the quals inherited from the parent, we might+ * have securityQuals associated with this particular child node.+ * (Currently this can only happen in appendrels originating from+ * UNION ALL; inheritance child tables don't have their own+ * securityQuals.) Pull any such securityQuals up into the+ * baserestrictinfo for the child. This is similar to+ * process_security_barrier_quals() for the parent rel, except+ * that we can't make any general deductions from such quals,+ * since they don't hold for the whole appendrel.+ */+ if (childRTE->securityQuals)+ {+ Index security_level = 0;++ foreach(lc, childRTE->securityQuals)+ {+ List *qualset = (List *) lfirst(lc);+ ListCell *lc2;++ foreach(lc2, qualset)+ {+ Expr *qual = (Expr *) lfirst(lc2);++ /*+ * not likely that we'd see constants here, so no+ * check+ */+ childquals = lappend(childquals,+ make_restrictinfo(qual,+ true,+ false,+ false,+ security_level,+ NULL, NULL,+ NULL));+ cq_min_security = Min(cq_min_security, security_level);+ }+ security_level++;+ }+ Assert(security_level <= root->qual_security_level);+ }++ /* Set child's version of baserestrictinfo. */+ childrel->baserestrictinfo = childquals;+ childrel->baserestrict_min_security = cq_min_security;++ if (have_const_false_cq)+ {+ /*+ * Some restriction clause reduced to constant FALSE or NULL after+ * substitution, so this child need not be scanned.+ */+ set_dummy_rel_pathlist(childrel);+ }++ return childrel;+}++/*+ * add_rowmark_junk_columns+ * Add necessary junk columns for rowmarked inheritance parent rel.+ *+ * These values are needed for locking of rels selected FOR UPDATE/SHARE, and+ * to do EvalPlanQual rechecking. See comments for PlanRowMark in+ * plannodes.h.+ */+static List *+add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc)+{+ List *tlist = root->processed_tlist;+ Var *var;+ char resname[32];+ TargetEntry *tle;++ if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))+ {+ /* Need to fetch TID */+ var = makeVar(rc->rti,+ SelfItemPointerAttributeNumber,+ TIDOID,+ -1,+ InvalidOid,+ 0);+ snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);+ }+ if (rc->allMarkTypes & (1 << ROW_MARK_COPY))+ {+ /* Need the whole row as a junk var */+ var = makeWholeRowVar(root->simple_rte_array[rc->rti],+ rc->rti,+ 0,+ false);+ snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);+ }++ /* For inheritance cases, always fetch the tableoid too. */+ var = makeVar(rc->rti,+ TableOidAttributeNumber,+ OIDOID,+ -1,+ InvalidOid,+ 0);+ snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);++ return tlist;+}diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.cnew file mode 100644index 0000000000..9502535e53--- /dev/null+++ b/src/backend/optimizer/util/appendinfo.c@@ -0,0 +1,851 @@+/*-------------------------------------------------------------------------+ *+ * appendinfo.c+ * Routines for mapping between append parent(s) and children+ *+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ *+ * IDENTIFICATION+ * src/backend/optimizer/path/appendinfo.c+ *+ *-------------------------------------------------------------------------+ */+#include "postgres.h"++#include "access/htup_details.h"+#include "access/sysattr.h"+#include "nodes/makefuncs.h"+#include "nodes/nodeFuncs.h"+#include "optimizer/appendinfo.h"+#include "parser/parsetree.h"+#include "utils/rel.h"+#include "utils/lsyscache.h"+#include "utils/syscache.h"++typedef struct+{+ PlannerInfo *root;+ int nappinfos;+ AppendRelInfo **appinfos;+} adjust_appendrel_attrs_context;++static void make_inh_translation_list(TupleDesc old_tupdesc,+ TupleDesc new_tupdesc,+ Oid from_rel, Oid to_rel,+ Index newvarno, List **translated_vars);+static Node *adjust_appendrel_attrs_mutator(Node *node,+ adjust_appendrel_attrs_context *context);+static Relids adjust_child_relids(Relids relids, int nappinfos,+ AppendRelInfo **appinfos);+static List *adjust_inherited_tlist(List *tlist,+ AppendRelInfo *context);++AppendRelInfo *+make_append_rel_info(RelOptInfo *parentrel, RangeTblEntry *parentrte,+ TupleDesc childdesc, Oid childoid, Oid childreltype,+ Index childRTindex)+{+ AppendRelInfo *appinfo = makeNode(AppendRelInfo);++ appinfo->parent_relid = parentrel->relid;+ appinfo->child_relid = childRTindex;+ appinfo->parent_reltype = parentrel->reltype;+ appinfo->child_reltype = childreltype;+ make_inh_translation_list(parentrel->tupdesc, childdesc,+ parentrte->relid, childoid,+ childRTindex,+ &appinfo->translated_vars);+ appinfo->parent_reloid = parentrte->relid;++ return appinfo;+}++/*+ * adjust_appendrel_attrs+ * Copy the specified query or expression and translate Vars referring to a+ * parent rel to refer to the corresponding child rel instead. We also+ * update rtindexes appearing outside Vars, such as resultRelation and+ * jointree relids.+ *+ * Note: this is only applied after conversion of sublinks to subplans,+ * so we don't need to cope with recursion into sub-queries.+ *+ * Note: this is not hugely different from what pullup_replace_vars() does;+ * maybe we should try to fold the two routines together.+ */+Node *+adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,+ AppendRelInfo **appinfos)+{+ Node *result;+ adjust_appendrel_attrs_context context;++ context.root = root;+ context.nappinfos = nappinfos;+ context.appinfos = appinfos;++ /* If there's nothing to adjust, don't call this function. */+ Assert(nappinfos >= 1 && appinfos != NULL);++ /*+ * Must be prepared to start with a Query or a bare expression tree.+ */+ if (node && IsA(node, Query))+ {+ Query *newnode;+ int cnt;++ newnode = query_tree_mutator((Query *) node,+ adjust_appendrel_attrs_mutator,+ (void *) &context,+ QTW_IGNORE_RC_SUBQUERIES);+ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ if (newnode->resultRelation == appinfo->parent_relid)+ {+ newnode->resultRelation = appinfo->child_relid;+ /* Fix tlist resnos too, if it's inherited UPDATE */+ if (newnode->commandType == CMD_UPDATE)+ newnode->targetList =+ adjust_inherited_tlist(newnode->targetList,+ appinfo);+ break;+ }+ }++ result = (Node *) newnode;+ }+ else+ result = adjust_appendrel_attrs_mutator(node, &context);++ return result;+}++static Node *+adjust_appendrel_attrs_mutator(Node *node,+ adjust_appendrel_attrs_context *context)+{+ AppendRelInfo **appinfos = context->appinfos;+ int nappinfos = context->nappinfos;+ int cnt;++ if (node == NULL)+ return NULL;+ if (IsA(node, Var))+ {+ Var *var = (Var *) copyObject(node);+ AppendRelInfo *appinfo = NULL;++ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ if (var->varno == appinfos[cnt]->parent_relid)+ {+ appinfo = appinfos[cnt];+ break;+ }+ }++ if (var->varlevelsup == 0 && appinfo)+ {+ var->varno = appinfo->child_relid;+ var->varnoold = appinfo->child_relid;+ if (var->varattno > 0)+ {+ Node *newnode;++ if (var->varattno > list_length(appinfo->translated_vars))+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",+ var->varattno, get_rel_name(appinfo->parent_reloid));+ newnode = copyObject(list_nth(appinfo->translated_vars,+ var->varattno - 1));+ if (newnode == NULL)+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",+ var->varattno, get_rel_name(appinfo->parent_reloid));+ return newnode;+ }+ else if (var->varattno == 0)+ {+ /*+ * Whole-row Var: if we are dealing with named rowtypes, we+ * can use a whole-row Var for the child table plus a coercion+ * step to convert the tuple layout to the parent's rowtype.+ * Otherwise we have to generate a RowExpr.+ */+ if (OidIsValid(appinfo->child_reltype))+ {+ Assert(var->vartype == appinfo->parent_reltype);+ if (appinfo->parent_reltype != appinfo->child_reltype)+ {+ ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);++ r->arg = (Expr *) var;+ r->resulttype = appinfo->parent_reltype;+ r->convertformat = COERCE_IMPLICIT_CAST;+ r->location = -1;+ /* Make sure the Var node has the right type ID, too */+ var->vartype = appinfo->child_reltype;+ return (Node *) r;+ }+ }+ else+ {+ /*+ * Build a RowExpr containing the translated variables.+ *+ * In practice var->vartype will always be RECORDOID here,+ * so we need to come up with some suitable column names.+ * We use the parent RTE's column names.+ *+ * Note: we can't get here for inheritance cases, so there+ * is no need to worry that translated_vars might contain+ * some dummy NULLs.+ */+ RowExpr *rowexpr;+ List *fields;+ RangeTblEntry *rte;++ rte = rt_fetch(appinfo->parent_relid,+ context->root->parse->rtable);+ fields = copyObject(appinfo->translated_vars);+ rowexpr = makeNode(RowExpr);+ rowexpr->args = fields;+ rowexpr->row_typeid = var->vartype;+ rowexpr->row_format = COERCE_IMPLICIT_CAST;+ rowexpr->colnames = copyObject(rte->eref->colnames);+ rowexpr->location = -1;++ return (Node *) rowexpr;+ }+ }+ /* system attributes don't need any other translation */+ }+ return (Node *) var;+ }+ if (IsA(node, CurrentOfExpr))+ {+ CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);++ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ if (cexpr->cvarno == appinfo->parent_relid)+ {+ cexpr->cvarno = appinfo->child_relid;+ break;+ }+ }+ return (Node *) cexpr;+ }+ if (IsA(node, RangeTblRef))+ {+ RangeTblRef *rtr = (RangeTblRef *) copyObject(node);++ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ if (rtr->rtindex == appinfo->parent_relid)+ {+ rtr->rtindex = appinfo->child_relid;+ break;+ }+ }+ return (Node *) rtr;+ }+ if (IsA(node, JoinExpr))+ {+ /* Copy the JoinExpr node with correct mutation of subnodes */+ JoinExpr *j;+ AppendRelInfo *appinfo;++ j = (JoinExpr *) expression_tree_mutator(node,+ adjust_appendrel_attrs_mutator,+ (void *) context);+ /* now fix JoinExpr's rtindex (probably never happens) */+ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ appinfo = appinfos[cnt];++ if (j->rtindex == appinfo->parent_relid)+ {+ j->rtindex = appinfo->child_relid;+ break;+ }+ }+ return (Node *) j;+ }+ if (IsA(node, PlaceHolderVar))+ {+ /* Copy the PlaceHolderVar node with correct mutation of subnodes */+ PlaceHolderVar *phv;++ phv = (PlaceHolderVar *) expression_tree_mutator(node,+ adjust_appendrel_attrs_mutator,+ (void *) context);+ /* now fix PlaceHolderVar's relid sets */+ if (phv->phlevelsup == 0)+ phv->phrels = adjust_child_relids(phv->phrels, context->nappinfos,+ context->appinfos);+ return (Node *) phv;+ }++ /*+ * This is needed, because inheritance_make_rel_from_joinlist needs to+ * translate root->join_info_list executing make_rel_from_joinlist for a+ * given child.+ */+ if (IsA(node, SpecialJoinInfo))+ {+ SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node;+ SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo);++ memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo));+ newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand,+ context->nappinfos,+ context->appinfos);+ newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand,+ context->nappinfos,+ context->appinfos);+ newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand,+ context->nappinfos,+ context->appinfos);+ newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand,+ context->nappinfos,+ context->appinfos);+ newinfo->semi_rhs_exprs =+ (List *) expression_tree_mutator((Node *)+ oldinfo->semi_rhs_exprs,+ adjust_appendrel_attrs_mutator,+ (void *) context);+ return (Node *) newinfo;+ }++ /* Shouldn't need to handle planner auxiliary nodes here */+ Assert(!IsA(node, AppendRelInfo));+ Assert(!IsA(node, PlaceHolderInfo));+ Assert(!IsA(node, MinMaxAggInfo));++ /*+ * We have to process RestrictInfo nodes specially. (Note: although+ * set_append_rel_pathlist will hide RestrictInfos in the parent's+ * baserestrictinfo list from us, it doesn't hide those in joininfo.)+ */+ if (IsA(node, RestrictInfo))+ {+ RestrictInfo *oldinfo = (RestrictInfo *) node;+ RestrictInfo *newinfo = makeNode(RestrictInfo);++ /* Copy all flat-copiable fields */+ memcpy(newinfo, oldinfo, sizeof(RestrictInfo));++ /* Recursively fix the clause itself */+ newinfo->clause = (Expr *)+ adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);++ /* and the modified version, if an OR clause */+ newinfo->orclause = (Expr *)+ adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);++ /* adjust relid sets too */+ newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,+ context->nappinfos,+ context->appinfos);++ /*+ * Reset cached derivative fields, since these might need to have+ * different values when considering the child relation. Note we+ * don't reset left_ec/right_ec: each child variable is implicitly+ * equivalent to its parent, so still a member of the same EC if any.+ */+ newinfo->eval_cost.startup = -1;+ newinfo->norm_selec = -1;+ newinfo->outer_selec = -1;+ newinfo->left_em = NULL;+ newinfo->right_em = NULL;+ newinfo->scansel_cache = NIL;+ newinfo->left_bucketsize = -1;+ newinfo->right_bucketsize = -1;+ newinfo->left_mcvfreq = -1;+ newinfo->right_mcvfreq = -1;++ return (Node *) newinfo;+ }++ /*+ * NOTE: we do not need to recurse into sublinks, because they should+ * already have been converted to subplans before we see them.+ */+ Assert(!IsA(node, SubLink));+ Assert(!IsA(node, Query));++ return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,+ (void *) context);+}++/*+ * Substitute child relids for parent relids in a Relid set. The array of+ * appinfos specifies the substitutions to be performed.+ */+static Relids+adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)+{+ Bitmapset *result = NULL;+ int cnt;++ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ /* Remove parent, add child */+ if (bms_is_member(appinfo->parent_relid, relids))+ {+ /* Make a copy if we are changing the set. */+ if (!result)+ result = bms_copy(relids);++ result = bms_del_member(result, appinfo->parent_relid);+ result = bms_add_member(result, appinfo->child_relid);+ }+ }++ /* If we made any changes, return the modified copy. */+ if (result)+ return result;++ /* Otherwise, return the original set without modification. */+ return relids;+}++/*+ * Replace any relid present in top_parent_relids with its child in+ * child_relids. Members of child_relids can be multiple levels below top+ * parent in the partition hierarchy.+ */+Relids+adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,+ Relids child_relids, Relids top_parent_relids)+{+ AppendRelInfo **appinfos;+ int nappinfos;+ Relids parent_relids = NULL;+ Relids result;+ Relids tmp_result = NULL;+ int cnt;++ /*+ * If the given relids set doesn't contain any of the top parent relids,+ * it will remain unchanged.+ */+ if (!bms_overlap(relids, top_parent_relids))+ return relids;++ appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);++ /* Construct relids set for the immediate parent of the given child. */+ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);+ }++ /* Recurse if immediate parent is not the top parent. */+ if (!bms_equal(parent_relids, top_parent_relids))+ {+ tmp_result = adjust_child_relids_multilevel(root, relids,+ parent_relids,+ top_parent_relids);+ relids = tmp_result;+ }++ result = adjust_child_relids(relids, nappinfos, appinfos);++ /* Free memory consumed by any intermediate result. */+ if (tmp_result)+ bms_free(tmp_result);+ bms_free(parent_relids);+ pfree(appinfos);++ return result;+}++/*+ * adjust_appendrel_attrs_multilevel+ * Apply Var translations from a toplevel appendrel parent down to a child.+ *+ * In some cases we need to translate expressions referencing a parent relation+ * to reference an appendrel child that's multiple levels removed from it.+ */+Node *+adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,+ Relids child_relids,+ Relids top_parent_relids)+{+ AppendRelInfo **appinfos;+ Bitmapset *parent_relids = NULL;+ int nappinfos;+ int cnt;++ Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids));++ appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);++ /* Construct relids set for the immediate parent of given child. */+ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);+ }++ /* Recurse if immediate parent is not the top parent. */+ if (!bms_equal(parent_relids, top_parent_relids))+ node = adjust_appendrel_attrs_multilevel(root, node, parent_relids,+ top_parent_relids);++ /* Now translate for this child */+ node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);++ pfree(appinfos);++ return node;+}++/*+ * Construct the SpecialJoinInfo for a child-join by translating+ * SpecialJoinInfo for the join between parents. left_relids and right_relids+ * are the relids of left and right side of the join respectively.+ */+SpecialJoinInfo *+build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,+ Relids left_relids, Relids right_relids)+{+ SpecialJoinInfo *sjinfo = makeNode(SpecialJoinInfo);+ AppendRelInfo **left_appinfos;+ int left_nappinfos;+ AppendRelInfo **right_appinfos;+ int right_nappinfos;++ memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));+ left_appinfos = find_appinfos_by_relids(root, left_relids,+ &left_nappinfos);+ right_appinfos = find_appinfos_by_relids(root, right_relids,+ &right_nappinfos);++ sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand,+ left_nappinfos, left_appinfos);+ sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand,+ right_nappinfos,+ right_appinfos);+ sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand,+ left_nappinfos, left_appinfos);+ sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand,+ right_nappinfos,+ right_appinfos);+ sjinfo->semi_rhs_exprs = (List *) adjust_appendrel_attrs(root,+ (Node *) sjinfo->semi_rhs_exprs,+ right_nappinfos,+ right_appinfos);++ pfree(left_appinfos);+ pfree(right_appinfos);++ return sjinfo;+}++/*+ * find_appinfos_by_relids+ * Find AppendRelInfo structures for all relations specified by relids.+ *+ * The AppendRelInfos are returned in an array, which can be pfree'd by the+ * caller. *nappinfos is set to the number of entries in the array.+ */+AppendRelInfo **+find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)+{+ AppendRelInfo **appinfos;+ int cnt = 0;+ int i;++ *nappinfos = bms_num_members(relids);+ appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos);++ i = -1;+ while ((i = bms_next_member(relids, i)) >= 0)+ {+ AppendRelInfo *appinfo = root->append_rel_array[i];++ if (!appinfo)+ elog(ERROR, "child rel %d not found in append_rel_array", i);++ appinfos[cnt++] = appinfo;+ }+ return appinfos;+}++/*+ * make_inh_translation_list+ * Build the list of translations from parent Vars to child Vars for+ * an inheritance child.+ *+ * For paranoia's sake, we match type/collation as well as attribute name.+ */+static void+make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc,+ Oid from_rel, Oid to_rel,+ Index newvarno, List **translated_vars)+{+ List *vars = NIL;+ int oldnatts = old_tupdesc->natts;+ int newnatts = new_tupdesc->natts;+ int old_attno;+ int new_attno = 0;++ for (old_attno = 0; old_attno < oldnatts; old_attno++)+ {+ Form_pg_attribute att;+ char *attname;+ Oid atttypid;+ int32 atttypmod;+ Oid attcollation;++ att = TupleDescAttr(old_tupdesc, old_attno);+ if (att->attisdropped)+ {+ /* Just put NULL into this list entry */+ vars = lappend(vars, NULL);+ continue;+ }+ attname = NameStr(att->attname);+ atttypid = att->atttypid;+ atttypmod = att->atttypmod;+ attcollation = att->attcollation;++ /*+ * When we are generating the "translation list" for the parent table+ * of an inheritance set, no need to search for matches.+ */+ if (from_rel == to_rel)+ {+ vars = lappend(vars, makeVar(newvarno,+ (AttrNumber) (old_attno + 1),+ atttypid,+ atttypmod,+ attcollation,+ 0));+ continue;+ }++ /*+ * Otherwise we have to search for the matching column by name.+ * There's no guarantee it'll have the same column position, because+ * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.+ * However, in simple cases, the relative order of columns is mostly+ * the same in both relations, so try the column of newrelation that+ * follows immediately after the one that we just found, and if that+ * fails, let syscache handle it.+ */+ if (new_attno >= newnatts ||+ (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped ||+ strcmp(attname, NameStr(att->attname)) != 0)+ {+ HeapTuple newtup;++ newtup = SearchSysCacheAttName(to_rel, attname);+ if (!newtup)+ elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",+ attname, get_rel_name(to_rel));+ new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;+ ReleaseSysCache(newtup);++ att = TupleDescAttr(new_tupdesc, new_attno);+ }++ /* Found it, check type and collation match */+ if (atttypid != att->atttypid || atttypmod != att->atttypmod)+ elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",+ attname, get_rel_name(to_rel));+ if (attcollation != att->attcollation)+ elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",+ attname, get_rel_name(to_rel));++ vars = lappend(vars, makeVar(newvarno,+ (AttrNumber) (new_attno + 1),+ atttypid,+ atttypmod,+ attcollation,+ 0));+ new_attno++;+ }++ *translated_vars = vars;+}++/*+ * translate_col_privs+ * Translate a bitmapset representing per-column privileges from the+ * parent rel's attribute numbering to the child's.+ *+ * The only surprise here is that we don't translate a parent whole-row+ * reference into a child whole-row reference. That would mean requiring+ * permissions on all child columns, which is overly strict, since the+ * query is really only going to reference the inherited columns. Instead+ * we set the per-column bits for all inherited columns.+ */+Bitmapset *+translate_col_privs(const Bitmapset *parent_privs,+ List *translated_vars)+{+ Bitmapset *child_privs = NULL;+ bool whole_row;+ int attno;+ ListCell *lc;++ /* System attributes have the same numbers in all tables */+ for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)+ {+ if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,+ parent_privs))+ child_privs = bms_add_member(child_privs,+ attno - FirstLowInvalidHeapAttributeNumber);+ }++ /* Check if parent has whole-row reference */+ whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,+ parent_privs);++ /* And now translate the regular user attributes, using the vars list */+ attno = InvalidAttrNumber;+ foreach(lc, translated_vars)+ {+ Var *var = lfirst_node(Var, lc);++ attno++;+ if (var == NULL) /* ignore dropped columns */+ continue;+ if (whole_row ||+ bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,+ parent_privs))+ child_privs = bms_add_member(child_privs,+ var->varattno - FirstLowInvalidHeapAttributeNumber);+ }++ return child_privs;+}++/*+ * Adjust the targetlist entries of an inherited UPDATE operation+ *+ * The expressions have already been fixed, but we have to make sure that+ * the target resnos match the child table (they may not, in the case of+ * a column that was added after-the-fact by ALTER TABLE). In some cases+ * this can force us to re-order the tlist to preserve resno ordering.+ * (We do all this work in special cases so that preptlist.c is fast for+ * the typical case.)+ *+ * The given tlist has already been through expression_tree_mutator;+ * therefore the TargetEntry nodes are fresh copies that it's okay to+ * scribble on.+ *+ * Note that this is not needed for INSERT because INSERT isn't inheritable.+ */+static List *+adjust_inherited_tlist(List *tlist, AppendRelInfo *context)+{+ bool changed_it = false;+ ListCell *tl;+ List *new_tlist;+ bool more;+ int attrno;++ /* This should only happen for an inheritance case, not UNION ALL */+ Assert(OidIsValid(context->parent_reloid));++ /* Scan tlist and update resnos to match attnums of child rel */+ foreach(tl, tlist)+ {+ TargetEntry *tle = (TargetEntry *) lfirst(tl);+ Var *childvar;++ if (tle->resjunk)+ continue; /* ignore junk items */++ /* Look up the translation of this column: it must be a Var */+ if (tle->resno <= 0 ||+ tle->resno > list_length(context->translated_vars))+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",+ tle->resno, get_rel_name(context->parent_reloid));+ childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);+ if (childvar == NULL || !IsA(childvar, Var))+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",+ tle->resno, get_rel_name(context->parent_reloid));++ if (tle->resno != childvar->varattno)+ {+ tle->resno = childvar->varattno;+ changed_it = true;+ }+ }++ /*+ * If we changed anything, re-sort the tlist by resno, and make sure+ * resjunk entries have resnos above the last real resno. The sort+ * algorithm is a bit stupid, but for such a seldom-taken path, small is+ * probably better than fast.+ */+ if (!changed_it)+ return tlist;++ new_tlist = NIL;+ more = true;+ for (attrno = 1; more; attrno++)+ {+ more = false;+ foreach(tl, tlist)+ {+ TargetEntry *tle = (TargetEntry *) lfirst(tl);++ if (tle->resjunk)+ continue; /* ignore junk items */++ if (tle->resno == attrno)+ new_tlist = lappend(new_tlist, tle);+ else if (tle->resno > attrno)+ more = true;+ }+ }++ foreach(tl, tlist)+ {+ TargetEntry *tle = (TargetEntry *) lfirst(tl);++ if (!tle->resjunk)+ continue; /* here, ignore non-junk items */++ tle->resno = attrno;+ new_tlist = lappend(new_tlist, tle);+ attrno++;+ }++ return new_tlist;+}diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.cindex d50d86b252..8ce88876c4 100644--- a/src/backend/optimizer/util/pathnode.c+++ b/src/backend/optimizer/util/pathnode.c@@ -20,6 +20,7 @@
#include "foreign/fdwapi.h"
#include "nodes/extensible.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.cindex c23db9d78d..52a11f434f 100644--- a/src/backend/optimizer/util/relnode.c+++ b/src/backend/optimizer/util/relnode.c@@ -17,6 +17,7 @@
#include <limits.h>
#include "miscadmin.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.cindex bf17ae22ba..9120fcb0d3 100644--- a/src/backend/partitioning/partprune.c+++ b/src/backend/partitioning/partprune.c@@ -44,6 +44,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
diff --git a/src/include/optimizer/append.h b/src/include/optimizer/append.hnew file mode 100644index 0000000000..5b44530279--- /dev/null+++ b/src/include/optimizer/append.h@@ -0,0 +1,28 @@+/*-------------------------------------------------------------------------+ *+ * append.h+ * prototypes for append.c.+ *+ *+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/include/optimizer/append.h+ *+ *-------------------------------------------------------------------------+ */+#ifndef APPEND_H+#define APPEND_H++#include "nodes/relation.h"++/*+ * append.c+ * utilities for dealing with append relations+ */+extern void expand_append_rel(PlannerInfo *root,+ RelOptInfo *rel,+ RangeTblEntry *rte,+ Index rti);++#endif /* APPEND_H */diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.hnew file mode 100644index 0000000000..e205e78e6d--- /dev/null+++ b/src/include/optimizer/appendinfo.h@@ -0,0 +1,43 @@+/*-------------------------------------------------------------------------+ *+ * appendinfo.h+ * Routines for mapping expressions between append rel parent(s) and+ * children+ *+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/include/optimizer/appendinfo.h+ *+ *-------------------------------------------------------------------------+ */+#ifndef APPENDINFO_H+#define APPENDINFO_H++#include "nodes/plannodes.h"+#include "nodes/relation.h"+#include "utils/relcache.h"++extern AppendRelInfo *make_append_rel_info(RelOptInfo *parentrel,+ RangeTblEntry *parentrte,+ TupleDesc childdesc, Oid childoid, Oid childreltype,+ Index childRTindex);+extern Bitmapset *translate_col_privs(const Bitmapset *parent_privs,+ List *translated_vars);+extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,+ int nappinfos, AppendRelInfo **appinfos);++extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,+ Relids child_relids,+ Relids top_parent_relids);++extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root,+ Relids relids, int *nappinfos);++extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,+ SpecialJoinInfo *parent_sjinfo,+ Relids left_relids, Relids right_relids);+extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,+ Relids child_relids, Relids top_parent_relids);++#endif /* APPENDINFO_H */diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.hindex e8e21e2ed5..0e2cdbc539 100644--- a/src/include/optimizer/prep.h+++ b/src/include/optimizer/prep.h@@ -46,26 +46,5 @@ extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
* prototypes for prepunion.c
*/
extern RelOptInfo *plan_set_operations(PlannerInfo *root);
-extern void expand_append_rel(PlannerInfo *root,- RelOptInfo *rel,- RangeTblEntry *rte,- Index rti);-extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,- int nappinfos, AppendRelInfo **appinfos);-extern AppendRelInfo *make_append_rel_info(RelOptInfo *parentrel,- RangeTblEntry *parentrte,- TupleDesc childdesc, Oid childoid, Oid childreltype,- Index childRTindex);-extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,- Relids child_relids,- Relids top_parent_relids);-extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root,- Relids relids, int *nappinfos);--extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,- SpecialJoinInfo *parent_sjinfo,- Relids left_relids, Relids right_relids);-extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,- Relids child_relids, Relids top_parent_relids);
#endif /* PREP_H */
--
2.11.0
From b869df16ad56eb7970085fe6cbf20cd0d5ab3be8 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Mon, 15 Oct 2018 10:59:24 +0900
Subject: [PATCH v5 5/6] Teach planner to only process unpruned partitions
This adds a bitmapset field live_parts to RelOptInfo and stores
the partition indexes of non-dummy partitions in it, meaning it
contains indexes of only those partitions that have a non-NULL
RelOptInfo present in its parent's RelOptInfo's part_rels array.
This speeds up processing partitioned table's partitions compared
to going through the whole part_rels array in a number of places.
---
src/backend/optimizer/path/joinrels.c | 3 +++
src/backend/optimizer/plan/planner.c | 18 +++++++++---------
src/backend/optimizer/util/append.c | 8 ++++++++
src/backend/optimizer/util/relnode.c | 3 +++
src/backend/partitioning/partprune.c | 18 +++++-------------
src/include/nodes/relation.h | 2 ++
6 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.cindex 6e321ec9e7..76531a05cf 100644--- a/src/backend/optimizer/path/joinrels.c+++ b/src/backend/optimizer/path/joinrels.c@@ -1424,6 +1424,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
populate_joinrel_with_paths(root, child_rel1, child_rel2,
child_joinrel, child_sjinfo,
child_restrictlist);
+ if (!IS_DUMMY_REL(child_joinrel))+ joinrel->live_parts = bms_add_member(joinrel->live_parts,+ cnt_parts);
}
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.cindex 216cadfba9..c60c491b61 100644--- a/src/backend/optimizer/plan/planner.c+++ b/src/backend/optimizer/plan/planner.c@@ -6766,7 +6766,9 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
List *live_children = NIL;
/* Adjust each partition. */
- for (partition_idx = 0; partition_idx < rel->nparts; partition_idx++)+ partition_idx = -1;+ while ((partition_idx = bms_next_member(rel->live_parts,+ partition_idx)) >= 0)
{
RelOptInfo *child_rel = rel->part_rels[partition_idx];
ListCell *lc;
@@ -6774,9 +6776,7 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
int nappinfos;
List *child_scanjoin_targets = NIL;
- /* Skip processing pruned partitions. */- if (child_rel == NULL)- continue;+ Assert(child_rel != NULL);
/* Translate scan/join targets for this child. */
appinfos = find_appinfos_by_relids(root, child_rel->relids,
@@ -6857,7 +6857,6 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
PartitionwiseAggregateType patype,
GroupPathExtraData *extra)
{
- int nparts = input_rel->nparts;
int cnt_parts;
List *grouped_live_children = NIL;
List *partially_grouped_live_children = NIL;
@@ -6869,7 +6868,9 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
partially_grouped_rel != NULL);
/* Add paths for partitionwise aggregation/grouping. */
- for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)+ cnt_parts = -1;+ while ((cnt_parts = bms_next_member(input_rel->live_parts,+ cnt_parts)) >= 0)
{
RelOptInfo *child_input_rel = input_rel->part_rels[cnt_parts];
PathTarget *child_target = copy_pathtarget(target);
@@ -6879,9 +6880,8 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
RelOptInfo *child_grouped_rel;
RelOptInfo *child_partially_grouped_rel;
- /* Skip processing pruned partitions. */- if (child_input_rel == NULL)- continue;+ /* A live partition must have a RelOptInfo. */+ Assert(child_input_rel != NULL);
/* Input child rel must have a path */
Assert(child_input_rel->pathlist != NIL);
diff --git a/src/backend/optimizer/util/append.c b/src/backend/optimizer/util/append.cindex bbc579817a..2ca8e8d0ff 100644--- a/src/backend/optimizer/util/append.c+++ b/src/backend/optimizer/util/append.c@@ -307,6 +307,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
/* Perform pruning. */
partindexes = prune_append_rel_partitions(parentrel);
+ parentrel->live_parts = partindexes;
/* Must expand PlannerInfo arrays before we can add children. */
expand_planner_arrays(root, bms_num_members(partindexes));
@@ -347,6 +348,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
Assert(childrel != NULL);
parentrel->part_rels[i] = childrel;
+ /*+ * If partition is excluded by constraints, remove it from+ * live_parts, too.+ */+ if (IS_DUMMY_REL(childrel))+ parentrel->live_parts = bms_del_member(parentrel->live_parts, i);+
/* Close child relations, but keep locks */
heap_close(newrelation, NoLock);
}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.cindex 52a11f434f..6c7eafe6db 100644--- a/src/backend/optimizer/util/relnode.c+++ b/src/backend/optimizer/util/relnode.c@@ -1748,6 +1748,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
joinrel->partexprs[cnt] = partexpr;
joinrel->nullable_partexprs[cnt] = nullable_partexpr;
}
++ /* Partitions will be added by try_partitionwise_join. */+ joinrel->live_parts = NULL;
}
/*
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.cindex 9120fcb0d3..2ed5c9e8dd 100644--- a/src/backend/partitioning/partprune.c+++ b/src/backend/partitioning/partprune.c@@ -441,29 +441,21 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
memset(subplan_map, -1, nparts * sizeof(int));
subpart_map = (int *) palloc(nparts * sizeof(int));
memset(subpart_map, -1, nparts * sizeof(int));
- present_parts = NULL;+ present_parts = bms_copy(subpart->live_parts);- for (i = 0; i < nparts; i++)+ i = -1;+ while ((i = bms_next_member(present_parts, i)) >= 0)
{
RelOptInfo *partrel = subpart->part_rels[i];
int subplanidx;
int subpartidx;
- /* Skip processing pruned partitions. */- if (partrel == NULL)- continue;-+ Assert(partrel != NULL);
subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
+ /* Record finding this subplan */
if (subplanidx >= 0)
- {- present_parts = bms_add_member(present_parts, i);-- /* Record finding this subplan */
subplansfound = bms_add_member(subplansfound, subplanidx);
- }- else if (subpartidx >= 0)- present_parts = bms_add_member(present_parts, i);
}
pinfo = makeNode(PartitionedRelPruneInfo);
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.hindex 6b614e4794..7e5e4e2c6a 100644--- a/src/include/nodes/relation.h+++ b/src/include/nodes/relation.h@@ -714,6 +714,8 @@ typedef struct RelOptInfo
Oid *part_oids; /* partition OIDs */
struct RelOptInfo **part_rels; /* Array of RelOptInfos of partitions,
* stored in the same order of bounds */
+ Bitmapset *live_parts; /* Set of live partitions; contains indexes+ * into part_rels array */
List **partexprs; /* Non-nullable partition key expressions. */
List **nullable_partexprs; /* Nullable partition key expressions. */
List *partitioned_child_rels; /* List of RT indexes. */
--
2.11.0
From 289fff1a50deead4b2b743d62056b613a916c221 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 12 Oct 2018 19:12:53 +0900
Subject: [PATCH v5 6/6] Do not lock all partitions at the beginning
---
src/backend/optimizer/util/append.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/backend/optimizer/util/append.c b/src/backend/optimizer/util/append.cindex 2ca8e8d0ff..f1627aef33 100644--- a/src/backend/optimizer/util/append.c+++ b/src/backend/optimizer/util/append.c@@ -291,10 +291,6 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
parentrel->inh_root_parent :
parentRTindex;
- /* If root partitioned table, lock *all* partitions in the tree. */- if (parentRTindex == rootParentRTindex)- (void) find_all_inheritors(parentrte->relid, lockmode, NULL);-
/*
* Initialize partitioned_child_rels to contain this RT index.
*
@@ -328,8 +324,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
Relation newrelation;
RelOptInfo *childrel;
- /* Already locked above. */- newrelation = heap_open(childOID, NoLock);+ newrelation = heap_open(childOID, lockmode);
Assert(!RELATION_IS_OTHER_TEMP(newrelation));
/*
--
2.11.0
Attachments:
[text/plain] v5-0001-Store-inheritance-root-parent-index-in-otherrel-s.patch (2.5K, ../3be67028-a00a-502c-199a-da00eec8fb6e@lab.ntt.co.jp/2-v5-0001-Store-inheritance-root-parent-index-in-otherrel-s.patch)
download | inline diff:
From 0f66b82a6ab998c4d790a4e2a6437950edf7de18 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 26 Oct 2018 16:45:59 +0900
Subject: [PATCH v5 1/6] Store inheritance root parent index in otherrel's
RelOptInfo
Although it's set by build_simple_rel, it's not being used by any
code yet.
---
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/util/relnode.c | 14 ++++++++++++++
src/include/nodes/relation.h | 4 ++++
3 files changed, 19 insertions(+)
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.cindex 69731ccdea..53a657c0ae 100644--- a/src/backend/nodes/outfuncs.c+++ b/src/backend/nodes/outfuncs.c@@ -2371,6 +2371,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
WRITE_BOOL_FIELD(consider_partitionwise_join);
WRITE_BITMAPSET_FIELD(top_parent_relids);
WRITE_NODE_FIELD(partitioned_child_rels);
+ WRITE_UINT_FIELD(inh_root_parent);
}
static void
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.cindex 39f5729b91..29ba19349f 100644--- a/src/backend/optimizer/util/relnode.c+++ b/src/backend/optimizer/util/relnode.c@@ -215,9 +215,23 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->top_parent_relids = parent->top_parent_relids;
else
rel->top_parent_relids = bms_copy(parent->relids);
++ /*+ * For inheritance child relations, we also set inh_root_parent.+ * Note that 'parent' might itself be a child (a sub-partitioned+ * partition), in which case we simply use its value of+ * inh_root_parent.+ */+ if (parent->rtekind == RTE_RELATION)+ rel->inh_root_parent = parent->inh_root_parent > 0 ?+ parent->inh_root_parent :+ parent->relid;
}
else
+ {
rel->top_parent_relids = NULL;
+ rel->inh_root_parent = 0;+ }
/* Check type of rtable entry */
switch (rte->rtekind)
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.hindex 6fd24203dd..32ac3315a4 100644--- a/src/include/nodes/relation.h+++ b/src/include/nodes/relation.h@@ -703,6 +703,10 @@ typedef struct RelOptInfo
List **partexprs; /* Non-nullable partition key expressions. */
List **nullable_partexprs; /* Nullable partition key expressions. */
List *partitioned_child_rels; /* List of RT indexes. */
++ Index inh_root_parent; /* For otherrels, this is the RT index of+ * inheritance table mentioned in the query+ * from which this relation originated */
} RelOptInfo;
/*
--
2.11.0
[text/plain] v5-0002-Overhaul-inheritance-update-delete-planning.patch (44.9K, ../3be67028-a00a-502c-199a-da00eec8fb6e@lab.ntt.co.jp/3-v5-0002-Overhaul-inheritance-update-delete-planning.patch)
download | inline diff:
From f14e09f119ce85c344b581af6b1623982d7cc2b1 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 17 Oct 2018 11:18:12 +0900
Subject: [PATCH v5 2/6] Overhaul inheritance update/delete planning
With the current method, inheritance_planner, which handles the
planning for update/delete commands targeting inheritance trees,
performs the *whole* planning for each child table. That involves
translating the query tree to set a given child table in place of
the original inheritance root table as the query's target table,
followed by calling grouping_planner on the translated query.
That's inefficient for two reasons:
1. partprune.c cannot be used to perform partition pruning, because
it can only be invoked from query_planner. With the current
approach, query_planner only sees the individual partitions in
the query tree it receives, not the parent partitioned table.
This leaves each partition to be pruned using constraint
exclusion.
2. Repeated invocation of query_planner results in allocating large
amounts of memory, especially if there are many child tables.
Also, it involves repeatedly performing the same processing,
such jointree processing.
This commit addresses both of the issues by rewriting portions of
inheritance_planner and adding some supporting functionality to
allpath.c.
With the new implementation, inheritance_planner calls query_planner
only once at the beginning with the original unmodified query, which
creates the access paths of individual relations after pruning any
unnecessary partitions. If the query involves join against the target
relation, join paths are created for each target child relation by
replacing the original target table in the join tree by a given child
table. Join relations (RelOptInfos thereof) for all target child
relations are collected in a global list in PlannerInfo.
After creating the join paths for all target child relations, it
calls grouping_planner() on each child join relation to finish up the
paths such that they produce query's top-level target list expanded
per a given child relation's descriptor. grouping_planner()'s
interface is modified so that we can pass it what's called a
'planned_rel', a RelOptInfo that already contains the necessary
paths needed to produce its output.
This removes some existing code in inheritance_planner that dealt
with any subquery RTEs in the query. The rationale of that code
was that the subquery RTEs may change during each iteration of
planning (that is, for different children), so different iterations
better use different copies of those RTEs. That was handled by
making fresh copies of those RTEs for each iteration of planning
which were appended to the range table, accompanied by modifying all
expressions and auxiliary structures that referenced the original
subquery RTEs to instead reference the copies (that is, change the
varnos). This copying meant we would end up adding S * N new entries
to the original range table by the time we got to the last unpruned
child, where S is the number of subquery RTEs in the original query
and N the number of unpruned children. Since with the new code
we perform planning just once, I think we don't need this special
handling. Actually, there is a regression test output change due
to no longer having copies of subquery RTEs (see the diff of
partition_join.out file.)
Also we no longer need the hack in relation_excluded_by_constraints()
involving setting PlannerInfo.inhTargetKind to enable constraint
exclusion for target child relation is no longer needed.
This commit removes inheritance_planner in favor of rearranging
things such that, only the planning steps that affect 1 and 2 above
are repeated for unpruned child relations.
---
doc/src/sgml/ddl.sgml | 14 +-
src/backend/nodes/outfuncs.c | 1 -
src/backend/optimizer/path/allpaths.c | 212 +++++++++++++-
src/backend/optimizer/plan/createplan.c | 10 -
src/backend/optimizer/plan/planner.c | 400 +++++++--------------------
src/backend/optimizer/prep/prepjointree.c | 1 -
src/backend/optimizer/prep/prepunion.c | 33 ++-
src/backend/optimizer/util/plancat.c | 50 +---
src/include/nodes/relation.h | 18 +-
src/test/regress/expected/partition_join.out | 4 +-
10 files changed, 356 insertions(+), 387 deletions(-)
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgmlindex b5ed1b7939..8cb69b1a47 100644--- a/doc/src/sgml/ddl.sgml+++ b/doc/src/sgml/ddl.sgml@@ -3935,15 +3935,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
<note>
<para>
- Currently, pruning of partitions during the planning of an- <command>UPDATE</command> or <command>DELETE</command> command is- implemented using the constraint exclusion method (however, it is- controlled by the <literal>enable_partition_pruning</literal> rather than- <literal>constraint_exclusion</literal>) — see the following section- for details and caveats that apply.- </para>-- <para>
Execution-time partition pruning currently occurs for the
<literal>Append</literal> and <literal>MergeAppend</literal> node types.
</para>
@@ -3964,9 +3955,8 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
<para>
<firstterm>Constraint exclusion</firstterm> is a query optimization
- technique similar to partition pruning. While it is primarily used- for partitioning implemented using the legacy inheritance method, it can be- used for other purposes, including with declarative partitioning.+ technique similar to partition pruning. It is primarily used+ for partitioning implemented using the legacy inheritance method.
</para>
<para>
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.cindex 53a657c0ae..a95153fea7 100644--- a/src/backend/nodes/outfuncs.c+++ b/src/backend/nodes/outfuncs.c@@ -2309,7 +2309,6 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
WRITE_FLOAT_FIELD(limit_tuples, "%.0f");
WRITE_UINT_FIELD(qual_security_level);
- WRITE_ENUM_FIELD(inhTargetKind, InheritanceKind);
WRITE_BOOL_FIELD(hasJoinRTEs);
WRITE_BOOL_FIELD(hasLateralRTEs);
WRITE_BOOL_FIELD(hasDeletedRTEs);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.cindex 738bb30848..8de8d383f9 100644--- a/src/backend/optimizer/path/allpaths.c+++ b/src/backend/optimizer/path/allpaths.c@@ -36,6 +36,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
+#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
@@ -119,6 +120,9 @@ static void set_namedtuplestore_pathlist(PlannerInfo *root, RelOptInfo *rel,
static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
+static void inheritance_make_rel_from_joinlist(PlannerInfo *root,+ RelOptInfo *parent,+ List *joinlist);
static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery,
pushdown_safety_info *safetyInfo);
static bool recurse_pushdown_safe(Node *setOp, Query *topquery,
@@ -217,13 +221,28 @@ make_one_rel(PlannerInfo *root, List *joinlist)
/*
* Generate access paths for the entire join tree.
+ *+ * For UPDATE/DELETE on an inheritance parent, join paths should be+ * generated for each child result rel separately.
*/
- rel = make_rel_from_joinlist(root, joinlist);+ if (root->parse->resultRelation &&+ root->simple_rte_array[root->parse->resultRelation]->inh)+ {+ rel = root->simple_rel_array[root->parse->resultRelation];+ inheritance_make_rel_from_joinlist(root, rel, joinlist);+ Assert(list_length(root->inh_target_child_rels) ==+ list_length(root->inh_target_child_joinrels));+ }+ else+ {+ rel = make_rel_from_joinlist(root, joinlist);- /*- * The result should join all and only the query's base rels.- */- Assert(bms_equal(rel->relids, root->all_baserels));+ /*+ * The result should join all and only the query's base rels.+ */+ Assert(bms_equal(rel->relids, root->all_baserels));++ }
return rel;
}
@@ -1219,6 +1238,63 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
set_rel_consider_parallel(root, childrel, childRTE);
/*
+ * If the root parent is the result relation, we need a reltarget+ * for the child that will be suitable to use the child as+ * target relation.+ */+ if (childrel->inh_root_parent > 0 &&+ childrel->inh_root_parent == root->parse->resultRelation)+ {+ Query *parse,+ *orig_parse = root->parse;+ List *tlist;+ List *other_exprs;+ ListCell *lc2;+ Relids child_relids,+ top_parent_relids;++ /*+ * We'd like to build the reltarget afresh; save the translated+ * version of parent's expressions aside.+ */+ other_exprs = childrel->reltarget->exprs;+ childrel->reltarget->exprs = NIL;++ /*+ * Translate the original query's expressions to this child,+ * mainly for updating the childrel's reltarget so that it+ * contains necessary expressions for using childrel as target+ * relation correctly. We need to use multilevel translation+ * as we can only access the original version of the query.+ */+ child_relids = bms_make_singleton(appinfo->child_relid);+ top_parent_relids = bms_make_singleton(childrel->inh_root_parent);+ parse = (Query *)+ adjust_appendrel_attrs_multilevel(root,+ (Node *) root->parse,+ child_relids,+ top_parent_relids);+ root->parse = parse;+ tlist = preprocess_targetlist(root);+ build_base_rel_tlists(root, tlist);+ root->parse = orig_parse;++ /*+ * Some of the expressions in parent's reltarget might not be+ * in child's just built expressions. Check to add any that are+ * missing.+ */+ foreach(lc2, other_exprs)+ {+ Expr *expr = lfirst(lc2);++ if (!list_member(childrel->reltarget->exprs, expr))+ childrel->reltarget->exprs =+ lappend(childrel->reltarget->exprs, expr);+ }+ }++ /*
* Compute the child's size.
*/
set_rel_size(root, childrel, childRTindex, childRTE);
@@ -2624,6 +2700,132 @@ generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_rows)
}
/*
+ * inheritance_make_rel_from_joinlist+ * Perform join planning for all non-dummy leaf inheritance children+ * in their role as query's target relation+ *+ * If a child relation is a partitioned table, its children are processed in+ * turn by recursively calling this function.+ */+static void+inheritance_make_rel_from_joinlist(PlannerInfo *root,+ RelOptInfo *parent,+ List *joinlist)+{+ List *join_info_list = root->join_info_list;+ ListCell *lc;+#ifdef USE_ASSERT_CHECKING+ int top_parent_relid = parent->inh_root_parent > 0 ?+ parent->inh_root_parent :+ parent->relid;+ Relids all_baserels;+#endif++ /*+ * For UPDATE/DELETE queries, the top parent can only ever be a table.+ * As a contrast, it could be a UNION ALL subquery in the case of SELECT.+ */+ Assert(planner_rt_fetch(top_parent_relid, root)->rtekind == RTE_RELATION);++ /* Nothing to do. */+ if (IS_DUMMY_REL(parent))+ return;++ foreach(lc, root->append_rel_list)+ {+ RelOptInfo *childrel;+ AppendRelInfo *appinfo = lfirst(lc);+ List *translated_joinlist;+ Relids child_relids,+ top_parent_relids;++ /* Only consider this parent's children. */+ if (appinfo->parent_relid != parent->relid)+ continue;++ childrel = find_base_rel(root, appinfo->child_relid);++ /* Ignore excluded/pruned children. */+ if (IS_DUMMY_REL(childrel))+ continue;++ /*+ * Sub-partitions have to be processed recursively, because+ * AppendRelInfos link sub-partitions to their immediate parents, not+ * the root partitioned table.+ */+ if (childrel->part_scheme != NULL)+ {+ inheritance_make_rel_from_joinlist(root, childrel, joinlist);+ continue;+ }++ /*+ * Modify joinlist such that relations joined to the top parent rel+ * appear to be joined to the child rel instead. Do the same for+ * any SpecialJoinInfo structs.+ */+ child_relids = bms_make_singleton(appinfo->child_relid);+ Assert(childrel->inh_root_parent > 0);+ top_parent_relids = bms_make_singleton(childrel->inh_root_parent);+ translated_joinlist = (List *)+ adjust_appendrel_attrs_multilevel(root,+ (Node *) joinlist,+ child_relids,+ top_parent_relids);+ root->join_info_list = (List *)+ adjust_appendrel_attrs_multilevel(root,+ (Node *) join_info_list,+ child_relids,+ top_parent_relids);++ /*+ * Since we added the child rel directly into the join tree, we must+ * modify it to be a "base" rel instead of an "other" rel, which the+ * join planning code expects the relations being joined to be.+ *+ * NB: Do we need to change the child EC members to be marked+ * as non-child somehow?+ */+ childrel->reloptkind = RELOPT_BASEREL;++ /* Reset join planning specific data structures. */+ root->join_rel_list = NIL;+ root->join_rel_hash = NULL;++ /* Perform join planning and save the resulting RelOptInfo. */+ childrel = make_rel_from_joinlist(root, translated_joinlist);++ /*+ * Remember this child target rel. inheritance_planner will perform+ * the remaining steps of planning for each child relation separately.+ * Specifically, it will call grouping_planner on every+ * RelOptInfo contained in the inh_target_child_joinrels list, each+ * of which represents the source of tuples to be modified for a given+ * target child rel.+ */+ root->inh_target_child_rels = lappend_int(root->inh_target_child_rels,+ appinfo->child_relid);+ root->inh_target_child_joinrels =+ lappend(root->inh_target_child_joinrels, childrel);++#ifdef USE_ASSERT_CHECKING+ /*+ * The following implements essentially the same Assert as in+ * make_one_rel, our caller.+ */+ all_baserels = bms_copy(root->all_baserels);+ all_baserels = bms_del_member(all_baserels, top_parent_relid);+ all_baserels = bms_add_member(all_baserels, appinfo->child_relid);+ Assert(bms_equal(childrel->relids, all_baserels));+#endif++ /* Restore the original for processing the next child. */+ root->join_info_list = join_info_list;+ }+}++/*
* make_rel_from_joinlist
* Build access paths using a "joinlist" to guide the join path search.
*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.cindex da7a92081a..28c4b53fea 100644--- a/src/backend/optimizer/plan/createplan.c+++ b/src/backend/optimizer/plan/createplan.c@@ -1998,12 +1998,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
/*
* During setrefs.c, we'll need the grouping_map to fix up the cols lists
* in GroupingFunc nodes. Save it for setrefs.c to use.
- *- * This doesn't work if we're in an inheritance subtree (see notes in- * create_modifytable_plan). Fortunately we can't be because there would- * never be grouping in an UPDATE/DELETE; but let's Assert that.
*/
- Assert(root->inhTargetKind == INHKIND_NONE);
Assert(root->grouping_map == NULL);
root->grouping_map = grouping_map;
@@ -2160,12 +2155,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
* with InitPlan output params. (We can't just do that locally in the
* MinMaxAgg node, because path nodes above here may have Agg references
* as well.) Save the mmaggregates list to tell setrefs.c to do that.
- *- * This doesn't work if we're in an inheritance subtree (see notes in- * create_modifytable_plan). Fortunately we can't be because there would- * never be aggregates in an UPDATE/DELETE; but let's Assert that.
*/
- Assert(root->inhTargetKind == INHKIND_NONE);
Assert(root->minmax_aggs == NIL);
root->minmax_aggs = best_path->mmaggregates;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.cindex c729a99f8b..93b8c761c2 100644--- a/src/backend/optimizer/plan/planner.c+++ b/src/backend/optimizer/plan/planner.c@@ -37,6 +37,7 @@
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
+#include "nodes/relation.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
@@ -126,7 +127,7 @@ 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 grouping_planner(PlannerInfo *root, bool inheritance_update,
- double tuple_fraction);+ RelOptInfo *planned_rel, double tuple_fraction);
static grouping_sets_data *preprocess_grouping_sets(PlannerInfo *root);
static List *remap_to_groupclause_idx(List *groupClause, List *gsets,
int *tleref_to_colnum_map);
@@ -630,7 +631,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->grouping_map = NULL;
root->minmax_aggs = NIL;
root->qual_security_level = 0;
- root->inhTargetKind = INHKIND_NONE;
root->hasRecursion = hasRecursion;
if (hasRecursion)
root->wt_param_id = SS_assign_special_param(root);
@@ -970,7 +970,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
rt_fetch(parse->resultRelation, parse->rtable)->inh)
inheritance_planner(root);
else
- grouping_planner(root, false, tuple_fraction);+ grouping_planner(root, false, NULL, tuple_fraction);
/*
* Capture the set of outer-level param IDs we have access to, for use in
@@ -1146,12 +1146,19 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr)
* inheritance set.
*
* We have to handle this case differently from cases where a source relation
- * is an inheritance set. Source inheritance is expanded at the bottom of the- * plan tree (see allpaths.c), but target inheritance has to be expanded at- * the top. The reason is that for UPDATE, each target relation needs a- * different targetlist matching its own column set. Fortunately,- * the UPDATE/DELETE target can never be the nullable side of an outer join,- * so it's OK to generate the plan this way.+ * is an inheritance set. That's mainly because for UPDATE, each target+ * relation needs a different targetlist matching its own column set. So, we+ * must modify the source scan/join path for each target relation such that it+ * produces the desired target list.+ *+ * The source scan/join paths for individual target relations are still+ * created in allpaths.c by first expanding the inheritance in the usual way+ * by set_append_rel_size, followed by join planning for each target relation+ * separately in make_one_rel. Finally, we apply grouping_planner here to each+ * child scan/join path so that it produces the desired targetlist.+ *+ * Fortunately, the UPDATE/DELETE target can never be the nullable side of an+ * outer join, so it's OK to generate the plan this way.
*
* Returns nothing; the useful output is in the Paths we attach to
* the (UPPERREL_FINAL, NULL) upperrel stored in *root.
@@ -1164,14 +1171,8 @@ inheritance_planner(PlannerInfo *root)
{
Query *parse = root->parse;
int top_parentRTindex = parse->resultRelation;
- Bitmapset *subqueryRTindexes;- Bitmapset *modifiableARIindexes;
int nominalRelation = -1;
Index rootRelation = 0;
- List *final_rtable = NIL;- int save_rel_array_size = 0;- RelOptInfo **save_rel_array = NULL;- AppendRelInfo **save_append_rel_array = NULL;
List *subpaths = NIL;
List *subroots = NIL;
List *resultRelations = NIL;
@@ -1179,70 +1180,60 @@ inheritance_planner(PlannerInfo *root)
List *returningLists = NIL;
List *rowMarks;
RelOptInfo *final_rel;
- ListCell *lc;- Index rti;+ ListCell *lc1,+ *lc2;
RangeTblEntry *parent_rte;
- PlannerInfo *parent_root;- Query *parent_parse;- Bitmapset *parent_relids = bms_make_singleton(top_parentRTindex);- PlannerInfo **parent_roots = NULL;+ List *tlist,+ *orig_tlist;+ standard_qp_extra qp_extra;+ RelOptInfo *planned_rel;+ List *partitioned_rels = NIL;+ /* Inheritance is never used for insert. */
Assert(parse->commandType != CMD_INSERT);
+ parent_rte = planner_rt_fetch(top_parentRTindex, root);
/*
- * 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- * same rangetable, but each instance must have its own set of subquery- * RTEs within the finished rangetable because (1) they are likely to get- * scribbled on during planning, and (2) it's not inconceivable that- * subqueries could get planned differently in different cases. We need- * not create duplicate copies of other RTE kinds, in particular not the- * target relations, because they don't have either of those issues. Not- * having to duplicate the target relations is important because doing so- * (1) would result in a rangetable of length O(N^2) for N targets, with- * at least O(N^3) work expended here; and (2) would greatly complicate- * management of the rowMarks list.+ * Generate the access paths for all relations mentioned in the query,+ * including the target inheritance set. When doing the join planning,+ * references in the join tree to the original target relation that's the+ * root parent of the inheritance tree is replaced by each of its+ * inheritance children and the resulting joinrel RelOptInfo's are+ * added to root->inh_target_child_joinrels.
*
- * To begin with, generate a bitmapset of the relids of the subquery RTEs.+ * Final planning steps (grouping_planner) are applied to the best path+ * of each of those child joinrels using a modified instance of the+ * original query for a given child target rel. All the paths so generated+ * are put into a list that will be controlled by a single ModifyTable+ * node. All the instances share the same rangetable.
*/
- subqueryRTindexes = NULL;- rti = 1;- foreach(lc, parse->rtable)- {- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);-- if (rte->rtekind == RTE_SUBQUERY)- subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);- rti++;- }
/*
- * 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.+ * Save the unmodified version of the query's targetlist to be used below+ * for passing to grouping_planner for each child target relation.
*/
- modifiableARIindexes = NULL;- if (subqueryRTindexes != NULL)- {- foreach(lc, root->append_rel_list)- {- AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);+ orig_tlist = list_copy(root->parse->targetList);- 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);- }+ /* Do the scan/join planning. */+ tlist = preprocess_targetlist(root);+ qp_extra.tlist = tlist;+ qp_extra.activeWindows = qp_extra.groupClause = NIL;+ planned_rel = query_planner(root, tlist, standard_qp_callback, &qp_extra);++ /*+ * If it turned out during query planning that all the children are dummy+ * (pruned or excluded by constraints), no need to do the steps below.+ * Let grouping_planner finish up the final path.+ */+ if (IS_DUMMY_REL(planned_rel))+ {+ grouping_planner(root, false, planned_rel, 0.0);+ return;
}
+ Assert(planned_rel->relid == top_parentRTindex);+ Assert(planned_rel->reloptkind == RELOPT_BASEREL);+
/*
* If the parent RTE is a partitioned table, we should use that as the
* nominal target relation, because the RTEs added for partitioned tables
@@ -1250,67 +1241,50 @@ 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;
rootRelation = top_parentRTindex;
+ partitioned_rels = lappend(partitioned_rels,+ list_make1_int(top_parentRTindex));
}
/*
- * The PlannerInfo for each child is obtained by translating the relevant- * members of the PlannerInfo for its immediate parent, which we find- * using the parent_relid in its AppendRelInfo. We save the PlannerInfo- * for each parent in an array indexed by relid for fast retrieval. Since- * the maximum number of parents is limited by the number of RTEs in the- * query, we use that number to allocate the array. An extra entry is- * needed since relids start from 1.+ * Get on with finalizing the path for each child target relation by+ * calling grouping_planner on its joinrel. Note that we're restoring+ * the query's targetlist to the original one for grouping_planner's+ * targetlist expansion steps to perform the expansion with individual+ * child descriptors.
*/
- parent_roots = (PlannerInfo **) palloc0((list_length(parse->rtable) + 1) *- sizeof(PlannerInfo *));- parent_roots[top_parentRTindex] = root;-- /*- * And now we can get on with generating a plan for each child table.- */- foreach(lc, root->append_rel_list)+ parse->targetList = orig_tlist;+ forboth(lc1, root->inh_target_child_rels,+ lc2, root->inh_target_child_joinrels)
{
- AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);+ AppendRelInfo *appinfo = root->append_rel_array[lfirst_int(lc1)];+ RelOptInfo *child_joinrel = lfirst(lc2);
PlannerInfo *subroot;
RangeTblEntry *child_rte;
RelOptInfo *sub_final_rel;
Path *subpath;
-- /* append_rel_list contains all append rels; ignore others */- if (!bms_is_member(appinfo->parent_relid, parent_relids))- continue;-- /*- * expand_inherited_rtentry() always processes a parent before any of- * that parent's children, so the parent_root for this relation should- * already be available.- */- parent_root = parent_roots[appinfo->parent_relid];- Assert(parent_root != NULL);- parent_parse = parent_root->parse;+ Relids child_relids = bms_make_singleton(appinfo->child_relid),+ top_parent_relids = bms_make_singleton(top_parentRTindex);
/*
* We need a working copy of the PlannerInfo so that we can control
* propagation of information back to the main copy.
*/
subroot = makeNode(PlannerInfo);
- memcpy(subroot, parent_root, sizeof(PlannerInfo));+ memcpy(subroot, root, sizeof(PlannerInfo));
/*
* 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,- * then fool around with subquery RTEs.+ * references to the parent RTE to refer to the current child RTE.
*/
subroot->parse = (Query *)
- adjust_appendrel_attrs(parent_root,- (Node *) parent_parse,- 1, &appinfo);+ adjust_appendrel_attrs_multilevel(root, (Node *) parse,+ child_relids,+ top_parent_relids);
/*
* If there are securityQuals attached to the parent, move them to the
@@ -1322,33 +1296,10 @@ inheritance_planner(PlannerInfo *root)
parent_rte->securityQuals = NIL;
/*
- * Mark whether we're planning a query to a partitioned table or an- * inheritance parent.+ * inheritance_make_rel_from_joinlist only adds the leaf partitions to+ * PlannerInfo.
*/
- subroot->inhTargetKind =- (rootRelation != 0) ? INHKIND_PARTITIONED : INHKIND_INHERITED;-- /*- * If this child is further partitioned, remember it as a parent.- * Since a partitioned table does not have any data, we don't need to- * create a plan for it, and we can stop processing it here. We do,- * however, need to remember its modified PlannerInfo for use when- * processing its children, since we'll update their varnos based on- * the delta from immediate parent to child, not from top to child.- *- * Note: a very non-obvious point is that we have not yet added- * duplicate subquery RTEs to the subroot's rtable. We mustn't,- * because then its children would have two sets of duplicates,- * confusing matters.- */- if (child_rte->inh)- {- Assert(child_rte->relkind == RELKIND_PARTITIONED_TABLE);- parent_relids = bms_add_member(parent_relids, appinfo->child_relid);- parent_roots[appinfo->child_relid] = subroot;-- continue;- }+ Assert(!child_rte->inh);
/*
* Set the nominal target relation of the ModifyTable node if not
@@ -1374,111 +1325,8 @@ inheritance_planner(PlannerInfo *root)
if (nominalRelation < 0)
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- * valid-looking.- */- while (list_length(subroot->parse->rtable) < list_length(final_rtable))- subroot->parse->rtable = lappend(subroot->parse->rtable,- makeNode(RangeTblEntry));-- /*- * 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.- */- if (final_rtable != NIL && subqueryRTindexes != NULL)- {- ListCell *lr;-- rti = 1;- foreach(lr, parent_parse->rtable)- {- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lr);-- if (bms_is_member(rti, subqueryRTindexes))- {- Index newrti;-- /*- * The RTE can't contain any references to its own RT- * index, except in its securityQuals, so we can save a- * few cycles by applying ChangeVarNodes to the rest of- * the rangetable before we append the RTE to it.- */- 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);- }- }- rte = copyObject(rte);- ChangeVarNodes((Node *) rte->securityQuals, rti, newrti, 0);- subroot->parse->rtable = lappend(subroot->parse->rtable,- rte);- }- rti++;- }- }-- /* There shouldn't be any OJ info to translate, as yet */- Assert(subroot->join_info_list == NIL);- /* and we haven't created PlaceHolderInfos, either */- Assert(subroot->placeholder_list == NIL);-- /* Generate Path(s) for accessing this result relation */- grouping_planner(subroot, true, 0.0 /* retrieve all tuples */ );+ /* Apply the top-level targetlist to child_joinrel's path. */+ grouping_planner(subroot, true, child_joinrel, 0.0);
/*
* Select cheapest path in case there's more than one. We always run
@@ -1490,45 +1338,10 @@ inheritance_planner(PlannerInfo *root)
subpath = sub_final_rel->cheapest_total_path;
/*
- * If this child rel was excluded by constraint exclusion, exclude it- * from the result plan.+ * child rel cannot be empty, or inheritance_make_rel_from_joinlist+ * wouldn't have put it in the list to begin with.
*/
- if (IS_DUMMY_PATH(subpath))- continue;-- /*- * If this is the first non-excluded child, its post-planning rtable- * 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;- else- final_rtable = list_concat(final_rtable,- list_copy_tail(subroot->parse->rtable,- list_length(final_rtable)));-- /*- * We need to collect all the RelOptInfos from all child plans into- * the main PlannerInfo, since setrefs.c will need them. We use the- * last child's simple_rel_array (previous ones are too short), so we- * 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++)- {- RelOptInfo *brel = 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;- save_append_rel_array = subroot->append_rel_array;-- /* Make sure any initplans from this rel get into the outer list */- root->init_plans = subroot->init_plans;+ Assert(!IS_DUMMY_PATH(subpath));
/* Build list of sub-paths */
subpaths = lappend(subpaths, subpath);
@@ -1560,36 +1373,6 @@ inheritance_planner(PlannerInfo *root)
*/
/*
- * 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;- }-- /*- * 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;- root->append_rel_array = save_append_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 = lfirst_node(RangeTblEntry, 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.
@@ -1629,6 +1412,12 @@ inheritance_planner(PlannerInfo *root)
* (inheritance_planner will create a single ModifyTable node covering all the
* target tables.)
*
+ * If non-NULL, planned_rel is a RelOptInfo containing paths for the query's+ * top-level joinrel, which the caller produced by itself. In that case, this+ * function only needs to adjust the targetlist of its cheapest_total_path.+ * The only caller that may pass such a RelOptInfo currently is+ * inheritance_planner.+ *
* tuple_fraction is the fraction of tuples we expect will be retrieved.
* tuple_fraction is interpreted as follows:
* 0: expect all tuples to be retrieved (normal case)
@@ -1647,6 +1436,7 @@ inheritance_planner(PlannerInfo *root)
*/
static void
grouping_planner(PlannerInfo *root, bool inheritance_update,
+ RelOptInfo *planned_rel,
double tuple_fraction)
{
Query *parse = root->parse;
@@ -1659,7 +1449,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
List *final_targets;
List *final_targets_contain_srfs;
bool final_target_parallel_safe;
- RelOptInfo *current_rel;+ RelOptInfo *current_rel = planned_rel;
RelOptInfo *final_rel;
ListCell *lc;
@@ -1699,6 +1489,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* special work for recursive unions is the responsibility of
* plan_set_operations.
*/
+ Assert(current_rel == NULL);
current_rel = plan_set_operations(root);
/*
@@ -1878,8 +1669,9 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* We also generate (in standard_qp_callback) pathkey representations
* of the query's sort clause, distinct clause, etc.
*/
- current_rel = query_planner(root, tlist,- standard_qp_callback, &qp_extra);+ if (current_rel == NULL)+ current_rel = query_planner(root, tlist,+ standard_qp_callback, &qp_extra);
/*
* Convert the query's result tlist into PathTarget format.
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.cindex cd6e11904e..78baec00dc 100644--- a/src/backend/optimizer/prep/prepjointree.c+++ b/src/backend/optimizer/prep/prepjointree.c@@ -917,7 +917,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->grouping_map = NULL;
subroot->minmax_aggs = NIL;
subroot->qual_security_level = 0;
- subroot->inhTargetKind = INHKIND_NONE;
subroot->hasRecursion = false;
subroot->wt_param_id = -1;
subroot->non_recursive_path = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.cindex d5720518a8..ef1f978889 100644--- a/src/backend/optimizer/prep/prepunion.c+++ b/src/backend/optimizer/prep/prepunion.c@@ -2253,8 +2253,39 @@ adjust_appendrel_attrs_mutator(Node *node,
context->appinfos);
return (Node *) phv;
}
++ /*+ * This is needed, because inheritance_make_rel_from_joinlist needs to+ * translate root->join_info_list executing make_rel_from_joinlist for a+ * given child.+ */+ if (IsA(node, SpecialJoinInfo))+ {+ SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node;+ SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo);++ memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo));+ newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand,+ context->nappinfos,+ context->appinfos);+ newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand,+ context->nappinfos,+ context->appinfos);+ newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand,+ context->nappinfos,+ context->appinfos);+ newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand,+ context->nappinfos,+ context->appinfos);+ newinfo->semi_rhs_exprs =+ (List *) expression_tree_mutator((Node *)+ oldinfo->semi_rhs_exprs,+ adjust_appendrel_attrs_mutator,+ (void *) context);+ return (Node *) newinfo;+ }+
/* Shouldn't need to handle planner auxiliary nodes here */
- Assert(!IsA(node, SpecialJoinInfo));
Assert(!IsA(node, AppendRelInfo));
Assert(!IsA(node, PlaceHolderInfo));
Assert(!IsA(node, MinMaxAggInfo));
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.cindex 0c88c90de4..ed0953f9e1 100644--- a/src/backend/optimizer/util/plancat.c+++ b/src/backend/optimizer/util/plancat.c@@ -1264,36 +1264,6 @@ get_relation_constraints(PlannerInfo *root,
}
}
- /*- * Append partition predicates, if any.- *- * For selects, partition pruning uses the parent table's partition bound- * descriptor, instead of constraint exclusion which is driven by the- * individual partition's partition constraint.- */- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)- {- List *pcqual = RelationGetPartitionQual(relation);-- if (pcqual)- {- /*- * Run the partition quals through const-simplification similar to- * check constraints. We skip canonicalize_qual, though, because- * partition quals should be in canonical form already; also,- * since the qual is in implicit-AND format, we'd have to- * explicitly convert it to explicit-AND format and back again.- */- pcqual = (List *) eval_const_expressions(root, (Node *) pcqual);-- /* Fix Vars to have the desired varno */- if (varno != 1)- ChangeVarNodes((Node *) pcqual, 1, varno, 0);-- result = list_concat(result, pcqual);- }- }-
heap_close(relation, NoLock);
return result;
@@ -1420,31 +1390,15 @@ relation_excluded_by_constraints(PlannerInfo *root,
switch (constraint_exclusion)
{
case CONSTRAINT_EXCLUSION_OFF:
-- /*- * Don't prune if feature turned off -- except if the relation is- * a partition. While partprune.c-style partition pruning is not- * yet in use for all cases (update/delete is not handled), it- * would be a UI horror to use different user-visible controls- * depending on such a volatile implementation detail. Therefore,- * for partitioned tables we use enable_partition_pruning to- * control this behavior.- */- if (root->inhTargetKind == INHKIND_PARTITIONED)- break;
return false;
case CONSTRAINT_EXCLUSION_PARTITION:
/*
* When constraint_exclusion is set to 'partition' we only handle
- * OTHER_MEMBER_RELs, or BASERELs in cases where the result target- * is an inheritance parent or a partitioned table.+ * OTHER_MEMBER_RELs.
*/
- if ((rel->reloptkind != RELOPT_OTHER_MEMBER_REL) &&- !(rel->reloptkind == RELOPT_BASEREL &&- root->inhTargetKind != INHKIND_NONE &&- rel->relid == root->parse->resultRelation))+ if (rel->reloptkind != RELOPT_OTHER_MEMBER_REL)
return false;
break;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.hindex 32ac3315a4..29339d6c72 100644--- a/src/include/nodes/relation.h+++ b/src/include/nodes/relation.h@@ -319,9 +319,6 @@ typedef struct PlannerInfo
Index qual_security_level; /* minimum security_level for quals */
/* Note: qual_security_level is zero if there are no securityQuals */
- InheritanceKind inhTargetKind; /* indicates if the target relation is an- * inheritance child or partition or a- * partitioned table */
bool hasJoinRTEs; /* true if any RTEs are RTE_JOIN kind */
bool hasLateralRTEs; /* true if any RTEs are marked LATERAL */
bool hasDeletedRTEs; /* true if any RTE was deleted from jointree */
@@ -343,6 +340,21 @@ typedef struct PlannerInfo
/* Does this query modify any partition key columns? */
bool partColsUpdated;
++ /*+ * The following fields are set during query planning portion of an+ * inherited UPDATE/DEELETE operation.+ */++ /* RT indexes of child target rels */+ List *inh_target_child_rels;++ /*+ * RelOptInfos representing the output of make_rel_from_joinlist() for+ * a each child rel in the above list, acting as the target relation in+ * place of the original parent target relation.+ */+ List *inh_target_child_joinrels;
} PlannerInfo;
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.outindex 3ba3aaf2d8..a539280851 100644--- a/src/test/regress/expected/partition_join.out+++ b/src/test/regress/expected/partition_join.out@@ -1781,7 +1781,7 @@ WHERE EXISTS (
Filter: (c IS NULL)
-> Nested Loop
-> Seq Scan on int4_tbl
- -> Subquery Scan on ss_1+ -> Subquery Scan on ss
-> Limit
-> Seq Scan on int8_tbl int8_tbl_1
-> Nested Loop Semi Join
@@ -1789,7 +1789,7 @@ WHERE EXISTS (
Filter: (c IS NULL)
-> Nested Loop
-> Seq Scan on int4_tbl
- -> Subquery Scan on ss_2+ -> Subquery Scan on ss
-> Limit
-> Seq Scan on int8_tbl int8_tbl_2
(28 rows)
--
2.11.0
[text/plain] v5-0003-Lazy-creation-of-RTEs-for-inheritance-children.patch (85.1K, ../3be67028-a00a-502c-199a-da00eec8fb6e@lab.ntt.co.jp/4-v5-0003-Lazy-creation-of-RTEs-for-inheritance-children.patch)
download | inline diff:
From 42c9c879f1793ea1fb5a6328e853139a40cfcc8d Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 17 Oct 2018 18:14:13 +0900
Subject: [PATCH v5 3/6] Lazy creation of RTEs for inheritance children
Currently, expand_inherited_tables called from subquery_planner opens
all inheritance child tables and adds them to the query's range
table and PlannerInfo in the form of AppendRelInfo's initially and
later RelOptInfo's. For partitioning, that's pretty wasteful, because
it's possible to determine only the partitions that will need to be
scanned using partition pruning, which doesn't require opening the
partitions themselves. However, the point at which the above
processing happens is too early to perform partition pruning.
This commit rearranges things within the planner so that inheritance
child relations can be added to the Query and PlannerInfo when
peforming set_append_rel_size on the parent table. Although the late
initialization approach only benefits the partitioning case, it seems
undesirable to do it only for partitioned tables, because it means
adding code to handle partitions specially at various places within
the planner. So, *all* inheritance parent tables are expanded at
set_append_rel_size. All unpruned partitions are added to Query
in the form of their RangeTblEntry's added to the range table and to
PlannerInfo in the form of an AppendRelInfo and a RelOptInfo for each.
For partitioning, although we don't create a RangeTblEntry and
RelOptInfo for pruned partitions at set_append_rel_size time,
partitionwise join code relies on the fact that even though partitions
may have been pruned, they'd still own a RelOptInfo to handle the
outer join case where the pruned partition appears on the nullable
side of join. Partitionwise join code deals with that by allocating
dummy RelOptInfos for pruned partitions that are based mostly on
their parent's properties.
There are some regression test diffs:
1. Caused by the fact that we no longer allocate a duplicate RT
entry for a partitioned table in its role as child and also that
the individual partition RT entries are now created in the order
in which their parent's are processed, whereas previously they'd
be added to the range table in the order of depth-first expansion
of inheritance tree.
2. Those in postgres_fdw.out are caused by the fact that junk columns
required for row marking are added to reltarget->exprs later than
user columns, because the row marking junk columns arent added
until after the inheritance is expanded which as of this commit
is later than it used to be.
---
contrib/postgres_fdw/expected/postgres_fdw.out | 32 +-
src/backend/optimizer/path/allpaths.c | 204 +-----
src/backend/optimizer/path/joinrels.c | 62 ++
src/backend/optimizer/plan/initsplan.c | 58 --
src/backend/optimizer/plan/planner.c | 54 +-
src/backend/optimizer/plan/setrefs.c | 6 +
src/backend/optimizer/prep/preptlist.c | 11 +-
src/backend/optimizer/prep/prepunion.c | 818 +++++++++++++++-------
src/backend/optimizer/util/Makefile | 2 +-
src/backend/optimizer/util/plancat.c | 41 +-
src/backend/optimizer/util/relnode.c | 80 +--
src/backend/partitioning/partprune.c | 43 +-
src/include/nodes/relation.h | 8 +
src/include/optimizer/pathnode.h | 1 +
src/include/optimizer/plancat.h | 2 +-
src/include/optimizer/prep.h | 15 +-
src/include/partitioning/partprune.h | 2 +-
src/test/regress/expected/join.out | 22 +-
src/test/regress/expected/partition_aggregate.out | 4 +-
19 files changed, 834 insertions(+), 631 deletions(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.outindex 21a2ef5ad3..c930bf7bab 100644--- a/contrib/postgres_fdw/expected/postgres_fdw.out+++ b/contrib/postgres_fdw/expected/postgres_fdw.out@@ -7085,15 +7085,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.f1, foo.ctid, foo.*, foo.tableoid
-> HashAggregate
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.*, foo.tableoid
Group Key: foo.f1
-> Append
-> Seq Scan on public.foo
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.*, foo.tableoid
-> Foreign Scan on public.foo2
- Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1+ Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
(23 rows)
@@ -7123,15 +7123,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.f1, foo.ctid, foo.*, foo.tableoid
-> HashAggregate
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.*, foo.tableoid
Group Key: foo.f1
-> Append
-> Seq Scan on public.foo
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.*, foo.tableoid
-> Foreign Scan on public.foo2
- Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1+ Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
(23 rows)
@@ -7160,15 +7160,15 @@ 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.f1, foo.ctid, foo.tableoid, foo.*
-> HashAggregate
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.tableoid, foo.*
Group Key: foo.f1
-> Append
-> Seq Scan on public.foo
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.tableoid, foo.*
-> Foreign Scan on public.foo2
- Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1+ Output: foo2.f1, foo2.ctid, foo2.tableoid, foo2.*
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
@@ -7178,15 +7178,15 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
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.f1, foo.ctid, foo.tableoid, foo.*
-> HashAggregate
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.tableoid, foo.*
Group Key: foo.f1
-> Append
-> Seq Scan on public.foo
- Output: foo.ctid, foo.*, foo.tableoid, foo.f1+ Output: foo.f1, foo.ctid, foo.tableoid, foo.*
-> Foreign Scan on public.foo2
- Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1+ Output: foo2.f1, foo2.ctid, foo2.tableoid, foo2.*
Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
(39 rows)
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.cindex 8de8d383f9..6ee800d84a 100644--- a/src/backend/optimizer/path/allpaths.c+++ b/src/backend/optimizer/path/allpaths.c@@ -44,7 +44,6 @@
#include "optimizer/var.h"
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
-#include "partitioning/partprune.h"
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
@@ -943,8 +942,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
double *parent_attrsizes;
int nattrs;
ListCell *l;
- Relids live_children = NULL;- bool did_pruning = false;
/* Guard against stack overflow due to overly deep inheritance tree. */
check_stack_depth();
@@ -952,29 +949,22 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
Assert(IS_SIMPLE_REL(rel));
/*
- * Initialize partitioned_child_rels to contain this RT index.- *- * Note that during the set_append_rel_pathlist() phase, we will bubble up- * the indexes of partitioned relations that appear down in the tree, so- * that when we've created Paths for all the children, the root- * partitioned table's list will contain all such indexes.+ * Add append children to the PlannerInfo.
*/
- if (rte->relkind == RELKIND_PARTITIONED_TABLE)- rel->partitioned_child_rels = list_make1_int(rti);+ expand_append_rel(root, rel, rte, rti);++ /* Did we find out that none of the children produce any output? */+ if (IS_DUMMY_REL(rel))+ return;
/*
- * If the partitioned relation has any baserestrictinfo quals then we- * attempt to use these quals to prune away partitions that cannot- * possibly contain any tuples matching these quals. In this case we'll- * store the relids of all partitions which could possibly contain a- * matching tuple, and skip anything else in the loop below.+ * If we only need to scan the parent because all of the children are+ * either pruned or excluded, do so right away and return.
*/
- if (enable_partition_pruning &&- rte->relkind == RELKIND_PARTITIONED_TABLE &&- rel->baserestrictinfo != NIL)+ if (!rte->inh)
{
- live_children = prune_append_rel_partitions(rel);- did_pruning = true;+ set_rel_size(root, rel, rti, rte);+ return;
}
/*
@@ -1014,12 +1004,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
int childRTindex;
RangeTblEntry *childRTE;
RelOptInfo *childrel;
- List *childquals;- Index cq_min_security;- bool have_const_false_cq;
ListCell *parentvars;
ListCell *childvars;
- ListCell *lc;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
@@ -1027,18 +1013,31 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
childRTindex = appinfo->child_relid;
childRTE = root->simple_rte_array[childRTindex];
+ Assert(childRTE != NULL);- /*- * The child rel's RelOptInfo was already created during- * add_base_rels_to_query.- */+ /* The child rel's RelOptInfo is created during expand_append_rel */
childrel = find_base_rel(root, childRTindex);
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
/*
- * Copy/Modify targetlist. Even if this child is deemed empty, we need- * its targetlist in case it falls on nullable side in a child-join- * because of partitionwise join.+ * Child relation may have be marked dummy if build_append_child_rel+ * found self-contradictory quals.+ */+ if (IS_DUMMY_REL(childrel))+ continue;++ if (relation_excluded_by_constraints(root, childrel, childRTE))+ {+ /*+ * This child need not be scanned, so we can omit it from the+ * appendrel.+ */+ set_dummy_rel_pathlist(childrel);+ continue;+ }++ /*+ * Copy/Modify targetlist.
*
* NB: the resulting childrel->reltarget->exprs may contain arbitrary
* expressions, which otherwise would not occur in a rel's targetlist.
@@ -1067,144 +1066,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
add_child_rel_equivalences(root, appinfo, rel, childrel);
childrel->has_eclass_joins = rel->has_eclass_joins;
- /*- * We have to copy the parent's quals to the child, with appropriate- * substitution of variables. However, only the baserestrictinfo- * quals are needed before we can check for constraint exclusion; so- * do that first and then check to see if we can disregard this child.- *- * The child rel's targetlist might contain non-Var expressions, which- * means that substitution into the quals could produce opportunities- * for const-simplification, and perhaps even pseudoconstant quals.- * Therefore, transform each RestrictInfo separately to see if it- * reduces to a constant or pseudoconstant. (We must process them- * separately to keep track of the security level of each qual.)- */- childquals = NIL;- cq_min_security = UINT_MAX;- have_const_false_cq = false;- foreach(lc, rel->baserestrictinfo)- {- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);- Node *childqual;- ListCell *lc2;-- Assert(IsA(rinfo, RestrictInfo));- childqual = adjust_appendrel_attrs(root,- (Node *) rinfo->clause,- 1, &appinfo);- childqual = eval_const_expressions(root, childqual);- /* check for flat-out constant */- if (childqual && IsA(childqual, Const))- {- if (((Const *) childqual)->constisnull ||- !DatumGetBool(((Const *) childqual)->constvalue))- {- /* Restriction reduces to constant FALSE or NULL */- have_const_false_cq = true;- break;- }- /* Restriction reduces to constant TRUE, so drop it */- continue;- }- /* might have gotten an AND clause, if so flatten it */- foreach(lc2, make_ands_implicit((Expr *) childqual))- {- Node *onecq = (Node *) lfirst(lc2);- bool pseudoconstant;-- /* check for pseudoconstant (no Vars or volatile functions) */- pseudoconstant =- !contain_vars_of_level(onecq, 0) &&- !contain_volatile_functions(onecq);- if (pseudoconstant)- {- /* tell createplan.c to check for gating quals */- root->hasPseudoConstantQuals = true;- }- /* reconstitute RestrictInfo with appropriate properties */- childquals = lappend(childquals,- make_restrictinfo((Expr *) onecq,- rinfo->is_pushed_down,- rinfo->outerjoin_delayed,- pseudoconstant,- rinfo->security_level,- NULL, NULL, NULL));- /* track minimum security level among child quals */- cq_min_security = Min(cq_min_security, rinfo->security_level);- }- }-- /*- * In addition to the quals inherited from the parent, we might have- * securityQuals associated with this particular child node.- * (Currently this can only happen in appendrels originating from- * UNION ALL; inheritance child tables don't have their own- * securityQuals, see expand_inherited_rtentry().) Pull any such- * securityQuals up into the baserestrictinfo for the child. This is- * similar to process_security_barrier_quals() for the parent rel,- * except that we can't make any general deductions from such quals,- * since they don't hold for the whole appendrel.- */- if (childRTE->securityQuals)- {- Index security_level = 0;-- foreach(lc, childRTE->securityQuals)- {- List *qualset = (List *) lfirst(lc);- ListCell *lc2;-- foreach(lc2, qualset)- {- Expr *qual = (Expr *) lfirst(lc2);-- /* not likely that we'd see constants here, so no check */- childquals = lappend(childquals,- make_restrictinfo(qual,- true, false, false,- security_level,- NULL, NULL, NULL));- cq_min_security = Min(cq_min_security, security_level);- }- security_level++;- }- Assert(security_level <= root->qual_security_level);- }-- /*- * OK, we've got all the baserestrictinfo quals for this child.- */- childrel->baserestrictinfo = childquals;- childrel->baserestrict_min_security = cq_min_security;-- if (have_const_false_cq)- {- /*- * Some restriction clause reduced to constant FALSE or NULL after- * substitution, so this child need not be scanned.- */- set_dummy_rel_pathlist(childrel);- continue;- }-- if (did_pruning && !bms_is_member(appinfo->child_relid, live_children))- {- /* This partition was pruned; skip it. */- set_dummy_rel_pathlist(childrel);- continue;- }-- if (relation_excluded_by_constraints(root, childrel, childRTE))- {- /*- * This child need not be scanned, so we can omit it from the- * appendrel.- */- set_dummy_rel_pathlist(childrel);- continue;- }-
/* CE failed, so finish copying/modifying join quals. */
childrel->joininfo = (List *)
adjust_appendrel_attrs(root,
@@ -1275,7 +1136,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
child_relids,
top_parent_relids);
root->parse = parse;
- tlist = preprocess_targetlist(root);+ tlist = preprocess_targetlist(root, true);
build_base_rel_tlists(root, tlist);
root->parse = orig_parse;
@@ -2744,6 +2605,7 @@ inheritance_make_rel_from_joinlist(PlannerInfo *root,
continue;
childrel = find_base_rel(root, appinfo->child_relid);
+ Assert(childrel != NULL);
/* Ignore excluded/pruned children. */
if (IS_DUMMY_REL(childrel))
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.cindex d3d21fed5d..da0831de4e 100644--- a/src/backend/optimizer/path/joinrels.c+++ b/src/backend/optimizer/path/joinrels.c@@ -20,6 +20,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/prep.h"
+#include "optimizer/tlist.h"
#include "partitioning/partbounds.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -46,6 +47,9 @@ static void try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1,
List *parent_restrictlist);
static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel,
bool strict_op);
+static RelOptInfo *build_dummy_partition_rel(PlannerInfo *root,+ RelOptInfo *parent,+ int partidx);
/*
@@ -1376,6 +1380,11 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
AppendRelInfo **appinfos;
int nappinfos;
+ if (child_rel1 == NULL)+ child_rel1 = build_dummy_partition_rel(root, rel1, cnt_parts);+ if (child_rel2 == NULL)+ child_rel2 = build_dummy_partition_rel(root, rel2, cnt_parts);+
/* We should never try to join two overlapping sets of rels. */
Assert(!bms_overlap(child_rel1->relids, child_rel2->relids));
child_joinrelids = bms_union(child_rel1->relids, child_rel2->relids);
@@ -1582,3 +1591,56 @@ match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op)
return -1;
}
++/*+ * build_dummy_partition_rel+ * Build a RelOptInfo and AppendRelInfo for a pruned partition+ *+ * This does not result in opening the relation or a range table entry being+ * created. Also, the RelOptInfo thus created is not stored anywhere else+ * beside the parent's part_rels array.+ *+ * The only reason this exists is because partition-wise join, in some cases,+ * needs a RelOptInfo to represent an empty relation that's on the nullable+ * side of an outer join, so that a Path representing the outer join can be+ * created.+ */+RelOptInfo *+build_dummy_partition_rel(PlannerInfo *root, RelOptInfo *parent, int partidx)+{+ RangeTblEntry *parentrte = root->simple_rte_array[parent->relid];+ RelOptInfo *rel;++ Assert(parent->part_rels[partidx] == NULL);++ /* Create minimally valid-looking RelOptInfo with parent's relid. */+ rel = makeNode(RelOptInfo);+ rel->reloptkind = RELOPT_OTHER_MEMBER_REL;+ rel->relid = parent->relid;+ rel->relids = bms_copy(parent->relids);+ if (parent->top_parent_relids)+ rel->top_parent_relids = parent->top_parent_relids;+ else+ rel->top_parent_relids = bms_copy(parent->relids);+ rel->reltarget = copy_pathtarget(parent->reltarget);+ parent->part_rels[partidx] = rel;+ mark_dummy_rel(rel);++ /*+ * Now we'll need a (no-op) AppendRelInfo for parent, because we're+ * setting the dummy partition's relid to be same as the parent's.+ */+ if (root->append_rel_array[parent->relid] == NULL)+ {+ /* Pass parent's info as for both the parent rel and child rel. */+ AppendRelInfo *appinfo = make_append_rel_info(parent, parentrte,+ parent->tupdesc,+ parentrte->relid,+ parent->reltype,+ parent->relid);++ root->append_rel_array[parent->relid] = appinfo;+ }++ return rel;+}diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.cindex 01335db511..7636aa82c4 100644--- a/src/backend/optimizer/plan/initsplan.c+++ b/src/backend/optimizer/plan/initsplan.c@@ -616,64 +616,6 @@ create_lateral_join_info(PlannerInfo *root)
bms_add_member(brel2->lateral_referencers, rti);
}
}
-- /*- * Lastly, propagate lateral_relids and lateral_referencers from appendrel- * parent rels to their child rels. We intentionally give each child rel- * the same minimum parameterization, even though it's quite possible that- * some don't reference all the lateral rels. This is because any append- * path for the parent will have to have the same parameterization for- * every child anyway, and there's no value in forcing extra- * reparameterize_path() calls. Similarly, a lateral reference to the- * parent prevents use of otherwise-movable join rels for each child.- */- for (rti = 1; rti < root->simple_rel_array_size; rti++)- {- RelOptInfo *brel = root->simple_rel_array[rti];- RangeTblEntry *brte = root->simple_rte_array[rti];-- /*- * Skip empty slots. Also skip non-simple relations i.e. dead- * relations.- */- if (brel == NULL || !IS_SIMPLE_REL(brel))- continue;-- /*- * In the case of table inheritance, the parent RTE is directly linked- * to every child table via an AppendRelInfo. In the case of table- * partitioning, the inheritance hierarchy is expanded one level at a- * time rather than flattened. Therefore, an other member rel that is- * a partitioned table may have children of its own, and must- * therefore be marked with the appropriate lateral info so that those- * children eventually get marked also.- */- Assert(brte);- if (brel->reloptkind == RELOPT_OTHER_MEMBER_REL &&- (brte->rtekind != RTE_RELATION ||- brte->relkind != RELKIND_PARTITIONED_TABLE))- continue;-- if (brte->inh)- {- foreach(lc, root->append_rel_list)- {- AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);- RelOptInfo *childrel;-- if (appinfo->parent_relid != rti)- continue;- childrel = root->simple_rel_array[appinfo->child_relid];- Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);- Assert(childrel->direct_lateral_relids == NULL);- childrel->direct_lateral_relids = brel->direct_lateral_relids;- Assert(childrel->lateral_relids == NULL);- childrel->lateral_relids = brel->lateral_relids;- Assert(childrel->lateral_referencers == NULL);- childrel->lateral_referencers = brel->lateral_referencers;- }- }- }
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.cindex 93b8c761c2..84746a9bc9 100644--- a/src/backend/optimizer/plan/planner.c+++ b/src/backend/optimizer/plan/planner.c@@ -23,6 +23,7 @@
#include "access/sysattr.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"
@@ -699,27 +700,24 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
}
if (rte->lateral)
root->hasLateralRTEs = true;
++ /*+ * While at it, also update the inh status. If the relation doesn't+ * or can't have any children, there is no point in letting inh be set+ * to true. Note that we do this before processing rowmarks, so that+ * the correct information for setting isParent field of PlanRowMarks.+ */+ if (rte->rtekind == RTE_RELATION)+ rte->inh = rte->inh && has_subclass(rte->relid);
}
/*
* 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 base relations not joins, so it's OK (and marginally more- * efficient) to do it after checking for join RTEs. We must do it 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.
@@ -1215,7 +1213,7 @@ inheritance_planner(PlannerInfo *root)
orig_tlist = list_copy(root->parse->targetList);
/* Do the scan/join planning. */
- tlist = preprocess_targetlist(root);+ tlist = preprocess_targetlist(root, false);
qp_extra.tlist = tlist;
qp_extra.activeWindows = qp_extra.groupClause = NIL;
planned_rel = query_planner(root, tlist, standard_qp_callback, &qp_extra);
@@ -1223,9 +1221,10 @@ inheritance_planner(PlannerInfo *root)
/*
* If it turned out during query planning that all the children are dummy
* (pruned or excluded by constraints), no need to do the steps below.
- * Let grouping_planner finish up the final path.+ * For regular inheritance case, it's possible that we only need to modify+ * the parent table. Let grouping_planner finish up the final path.
*/
- if (IS_DUMMY_REL(planned_rel))+ if (!parent_rte->inh || IS_DUMMY_REL(planned_rel))
{
grouping_planner(root, false, planned_rel, 0.0);
return;
@@ -1580,7 +1579,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
}
/* Preprocess targetlist */
- tlist = preprocess_targetlist(root);+ tlist = preprocess_targetlist(root, inheritance_update);
/*
* We are now done hacking up the query's targetlist. Most of the
@@ -1674,6 +1673,15 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
standard_qp_callback, &qp_extra);
/*
+ * Query planning may have added some columns to the top-level tlist,+ * which happens when there are row marks applied to inheritance+ * parent relations (additional junk columns needed for applying row+ * marks are added after expanding inheritance.)+ */+ if (list_length(tlist) < list_length(root->processed_tlist))+ tlist = root->processed_tlist;++ /*
* Convert the query's result tlist into PathTarget format.
*
* Note: it's desirable to not do this till after query_planner(),
@@ -2360,7 +2368,7 @@ preprocess_rowmarks(PlannerInfo *root)
newrc->allMarkTypes = (1 << newrc->markType);
newrc->strength = rc->strength;
newrc->waitPolicy = rc->waitPolicy;
- newrc->isParent = false;+ newrc->isParent = rte->inh;
prowmarks = lappend(prowmarks, newrc);
}
@@ -2385,7 +2393,7 @@ preprocess_rowmarks(PlannerInfo *root)
newrc->allMarkTypes = (1 << newrc->markType);
newrc->strength = LCS_NONE;
newrc->waitPolicy = LockWaitBlock; /* doesn't matter */
- newrc->isParent = false;+ newrc->isParent = rte->rtekind == RTE_RELATION ? rte->inh : false;
prowmarks = lappend(prowmarks, newrc);
}
@@ -6765,6 +6773,10 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
int nappinfos;
List *child_scanjoin_targets = NIL;
+ /* Skip processing pruned partitions. */+ if (child_rel == NULL)+ continue;+
/* Translate scan/join targets for this child. */
appinfos = find_appinfos_by_relids(root, child_rel->relids,
&nappinfos);
@@ -6866,6 +6878,10 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
RelOptInfo *child_grouped_rel;
RelOptInfo *child_partially_grouped_rel;
+ /* Skip processing pruned partitions. */+ if (child_input_rel == NULL)+ continue;+
/* Input child rel must have a path */
Assert(child_input_rel->pathlist != NIL);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.cindex 6d6ef1c376..454870609a 100644--- a/src/backend/optimizer/plan/setrefs.c+++ b/src/backend/optimizer/plan/setrefs.c@@ -326,6 +326,12 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
UPPERREL_FINAL, NULL)))
add_rtes_to_flat_rtable(rel->subroot, true);
}
+ /*+ * A NULL rel also means an unplanned subquery rte, so apply+ * flatten_unplanned_rtes.+ */+ else+ flatten_unplanned_rtes(glob, rte);
}
rti++;
}
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.cindex 8603feef2b..9c0a1c1b1c 100644--- a/src/backend/optimizer/prep/preptlist.c+++ b/src/backend/optimizer/prep/preptlist.c@@ -67,7 +67,7 @@ static List *expand_targetlist(List *tlist, int command_type,
* is also preprocessed (and updated in-place).
*/
List *
-preprocess_targetlist(PlannerInfo *root)+preprocess_targetlist(PlannerInfo *root, bool inheritance_update)
{
Query *parse = root->parse;
int result_relation = parse->resultRelation;
@@ -134,6 +134,15 @@ preprocess_targetlist(PlannerInfo *root)
if (rc->rti != rc->prti)
continue;
+ /*+ * For inheritance parent row marks, we defer adding junk columns+ * until we've added child row marks, because some children might+ * require different row mark types which affects which junk columns+ * are needed in the top-level tlist.+ */+ if (rc->isParent && !inheritance_update)+ continue;+
if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
{
/* Need to fetch TID */
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.cindex ef1f978889..c6817757e7 100644--- a/src/backend/optimizer/prep/prepunion.c+++ b/src/backend/optimizer/prep/prepunion.c@@ -39,15 +39,19 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
+#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
+#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parsetree.h"
+#include "partitioning/partprune.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
@@ -100,22 +104,22 @@ static List *generate_append_tlist(List *colTypes, List *colCollations,
List *refnames_tlist);
static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
- Index rti);+ Index rti, RelOptInfo *rel);
static void expand_partitioned_rtentry(PlannerInfo *root,
RangeTblEntry *parentrte,
- Index parentRTindex, Relation parentrel,- PlanRowMark *top_parentrc, LOCKMODE lockmode,- List **appinfos);-static void expand_single_inheritance_child(PlannerInfo *root,- RangeTblEntry *parentrte,- Index parentRTindex, Relation parentrel,- PlanRowMark *top_parentrc, Relation childrel,- List **appinfos, RangeTblEntry **childrte_p,- Index *childRTindex_p);-static void make_inh_translation_list(Relation oldrelation,- Relation newrelation,- Index newvarno,- List **translated_vars);+ Index parentRTindex, RelOptInfo *parentrel);+static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root,+ RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel,+ PlanRowMark *top_parentrc, Relation childrel);+static void make_inh_translation_list(TupleDesc old_tupdesc,+ TupleDesc new_tupdesc,+ Oid from_rel, Oid to_rel,+ Index newvarno, List **translated_vars);+static RelOptInfo *build_append_child_rel(PlannerInfo *root,+ RelOptInfo *parent,+ Index childRTindex);+static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc);
static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
List *translated_vars);
static Node *adjust_appendrel_attrs_mutator(Node *node,
@@ -1463,42 +1467,107 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
/*
- * 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.+ * expand_append_rel+ * This initializes RelOptInfos for an appendrel's child relations.+ *+ * 'rel' is the appendrel parent, whose range table entry ('rte') has been+ * marked to require adding children. An appendrel parent could either+ * be a subquery (if we flattened UNION ALL query) or a table that's known+ * to have inheritance children. The latter consists of both regular+ * inheritance parents and partitioned tables.+ *+ * For a subquery parent, there is not much to be done here because the+ * children's RTEs are already present in the query, so we just initialize+ * RelOptInfos for them. Also, the AppendRelInfos for child subqueries+ * have already been added.+ *+ * For tables, we need to add the children to the range table and initialize+ * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them. For+ * a partitioned parent, we only add the children remaining after pruning.+ * For regular inheritance parents, we find the children using+ * find_all_inheritors and add all of them.+ *+ * If it turns out that there are no children, then we set rte->inh to false+ * to let the caller know that only the parent table needs to be scanned. The+ * caller can accordingly switch to a non-Append path. For a partitioned+ * parent, that means an empty relation because parents themselves contain no+ * data.+ *+ * For the regular inheritance case, the parent also gets another RTE with+ * inh = false to represent it as an appendrel child. The original RTE is+ * considered to represent the whole inheritance set.
*/
void
-expand_inherited_tables(PlannerInfo *root)+expand_append_rel(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,+ Index rti)
{
- Index nrtes;- Index rti;- ListCell *rl;+ Assert(rte->inh);+ /* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */+ Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY);
/*
- * 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.+ * UNION ALL children already got RTEs and AppendRelInfos, so just build+ * RelOptInfos and return.+ *+ * It might be a bit odd that this code is in this, because there is+ * nothing to expand really.
*/
- nrtes = list_length(root->parse->rtable);- rl = list_head(root->parse->rtable);- for (rti = 1; rti <= nrtes; rti++)+ if (rte->rtekind == RTE_SUBQUERY)
{
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);+ ListCell *l;- expand_inherited_rtentry(root, rte, rti);- rl = lnext(rl);+ /*+ * We don't need to use expand_planner_arrays in this case, because+ * no new child RTEs are created. setup_simple_rel_arrays() and+ * setup_append_rel_array would've considered these child RTEs when+ * allocating space for various arrays.+ */+ foreach(l, root->append_rel_list)+ {+ AppendRelInfo *appinfo = lfirst(l);+ Index childRTindex = appinfo->child_relid;++ if (appinfo->parent_relid != rti)+ continue;++ Assert(childRTindex < root->simple_rel_array_size);+ Assert(root->simple_rte_array[childRTindex] != NULL);++ /*+ * We set the correct value of baserestricinfo and+ * baserestrict_min_security below.+ */+ root->simple_rel_array[childRTindex] =+ build_append_child_rel(root, rel, appinfo->child_relid);+ }+ }+ else+ {+ Assert(rte->rtekind == RTE_RELATION);+ Assert(has_subclass(rte->relid));++ /*+ * The rewriter should already have obtained an appropriate lock on+ * each relation named in the query. However, for each child relation+ * we add to the query, we must obtain an appropriate lock, because+ * this will be the first use of those relations in the+ * parse/rewrite/plan pipeline. Child rels should use the same+ * lockmode as their parent.+ */+ Assert(rte->rellockmode != NoLock);++ if (rte->relkind == RELKIND_PARTITIONED_TABLE)+ expand_partitioned_rtentry(root, rte, rti, rel);+ else+ expand_inherited_rtentry(root, rte, rti, rel);
}
}
/*
* 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- * rangetable, and build AppendRelInfo nodes for all the child tables- * and add them to root->append_rel_list. If not, clear the entry's- * "inh" flag to prevent later code from looking for AppendRelInfos.+ * Add entries for all the child tables to the query's rangetable, and+ * build AppendRelInfo nodes for all the child tables and add them to+ * root->append_rel_list.
*
* Note that the original RTE is considered to represent the whole
* inheritance set. The first of the generated RTEs is an RTE for the same
@@ -1509,45 +1578,22 @@ expand_inherited_tables(PlannerInfo *root)
* regular inheritance, a parent RTE must always have at least two associated
* AppendRelInfos: one corresponding to the parent table as a simple member of
* inheritance set and one or more corresponding to the actual children.
- * Since a partitioned table is not scanned, it might have only one associated- * AppendRelInfo.
*/
static void
-expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)+expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti,+ RelOptInfo *rel)
{
Oid parentOID;
PlanRowMark *oldrc;
- Relation oldrelation;- LOCKMODE lockmode;+ LOCKMODE lockmode = rte->rellockmode;
List *inhOIDs;
ListCell *l;
+ int num_children;+ int num_children_added = 0;- /* Does RT entry allow inheritance? */- if (!rte->inh)- return;- /* Ignore any already-expanded UNION ALL nodes */- if (rte->rtekind != RTE_RELATION)- {- Assert(rte->rtekind == RTE_SUBQUERY);- return;- }- /* Fast path for common case of childless table */+ Assert(rte->rtekind == RTE_RELATION);+ Assert(lockmode != NoLock);
parentOID = rte->relid;
- if (!has_subclass(parentOID))- {- /* Clear flag before returning */- rte->inh = false;- return;- }-- /*- * The rewriter should already have obtained an appropriate lock on each- * relation named in the query. However, for each child relation we add- * to the query, we must obtain an appropriate lock, because this will be- * the first use of those relations in the parse/rewrite/plan pipeline.- * Child rels should use the same lockmode as their parent.- */- lockmode = rte->rellockmode;
/* Scan for all members of inheritance set, acquire needed locks */
inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
@@ -1557,7 +1603,8 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
* case. This could happen despite above has_subclass() check, if table
* once had a child but no longer does.
*/
- if (list_length(inhOIDs) < 2)+ num_children = list_length(inhOIDs);+ if (num_children < 2)
{
/* Clear flag before returning */
rte->inh = false;
@@ -1565,277 +1612,246 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
}
/*
- * If parent relation is selected FOR UPDATE/SHARE, we need to mark its- * PlanRowMark as isParent = true, and generate a new PlanRowMark for each- * child.+ * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks+ * should've set isParent = true. We'll generate a new PlanRowMark for+ * each child.
*/
oldrc = get_plan_rowmark(root->rowMarks, rti);
- if (oldrc)- oldrc->isParent = true;+ Assert(oldrc == NULL || oldrc->isParent);
/*
- * Must open the parent relation to examine its tupdesc. We need not lock- * it; we assume the rewriter already did.+ * Must expand PlannerInfo arrays by num_children before we can add+ * children.
*/
- oldrelation = heap_open(parentOID, NoLock);+ expand_planner_arrays(root, num_children);- /* Scan the inheritance set and expand it */- if (RelationGetPartitionDesc(oldrelation) != NULL)+ foreach(l, inhOIDs)
{
- Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);+ Oid childOID = lfirst_oid(l);+ Relation newrelation;+ RelOptInfo *childrel;++ /* Already locked above. */+ newrelation = heap_open(childOID, NoLock);
/*
- * If this table has partitions, recursively expand them in the order- * in which they appear in the PartitionDesc. While at it, also- * extract the partition key columns of all the partitioned tables.+ * It is possible that the parent table has children that are temp+ * tables of other backends. We cannot safely access such tables+ * (because of buffering issues), and the best thing to do seems+ * to be to silently ignore them.
*/
- expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,- lockmode, &root->append_rel_list);- }- else- {- List *appinfos = NIL;- RangeTblEntry *childrte;- Index childRTindex;-- /*- * This table has no partitions. Expand any plain inheritance- * children in the order the OIDs were returned by- * find_all_inheritors.- */- foreach(l, inhOIDs)+ if (RELATION_IS_OTHER_TEMP(newrelation))
{
- Oid childOID = lfirst_oid(l);- Relation newrelation;-- /* Open rel if needed; we already have required locks */- if (childOID != parentOID)- newrelation = heap_open(childOID, NoLock);- else- newrelation = oldrelation;-- /*- * It is possible that the parent table has children that are temp- * tables of other backends. We cannot safely access such tables- * (because of buffering issues), and the best thing to do seems- * to be to silently ignore them.- */- if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))- {- heap_close(newrelation, lockmode);- continue;- }-- expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,- newrelation,- &appinfos, &childrte,- &childRTindex);-- /* Close child relations, but keep locks */- if (childOID != parentOID)- heap_close(newrelation, NoLock);+ heap_close(newrelation, lockmode);+ continue;
}
- /*- * If all the children were temp tables, pretend it's a- * non-inheritance situation; we don't need Append node in that case.- * The duplicate RTE we added for the parent table is harmless, so we- * don't bother to get rid of it; ditto for the useless PlanRowMark- * node.- */- if (list_length(appinfos) < 2)- rte->inh = false;- else- root->append_rel_list = list_concat(root->append_rel_list,- appinfos);+ childrel = add_inheritance_child_rel(root, rte, rti, rel, oldrc,+ newrelation);+ Assert(childrel != NULL);+ /* Close child relations, but keep locks */+ heap_close(newrelation, NoLock);+ num_children_added++;
}
- heap_close(oldrelation, NoLock);+ /*+ * If all children, including the parent (as child rel), were+ * excluded, mark the parent rel as empty. If all the children were temp+ * tables, pretend it's a non-inheritance situation; we don't need Append+ * node in that case. The duplicate RTE we added for the parent table is+ * harmless, so we don't bother to get rid of it; ditto for the useless+ * PlanRowMark node.+ */+ if (num_children_added == 0)+ mark_dummy_rel(rel);+ else if (num_children_added == 1)+ rte->inh = false;++ /*+ * Add junk columns needed by the row mark if any and also add the+ * relevant expressions to the root parent's reltarget.+ */+ if (oldrc)+ {+ List *tlist = add_rowmark_junk_columns(root, oldrc);++ build_base_rel_tlists(root, tlist);+ }
}
/*
* expand_partitioned_rtentry
- * Recursively expand an RTE for a partitioned table.+ * Prunes unnecessary partitions of a partitioned table and adds+ * remaining ones to the Query and the PlannerInfo
*
- * Note that RelationGetPartitionDispatchInfo will expand partitions in the- * same order as this code.+ * Partitions are added to the query in order in which they are found in+ * the parent's PartitionDesc.+ *+ * Note: even though only the unpruned partitions will be added to the+ * resulting plan, this still locks *all* partitions via find_all_inheritors+ * in order to avoid partitions being locked in a different order than other+ * places in the backend that may lock partitions.
*/
static void
expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
- Index parentRTindex, Relation parentrel,- PlanRowMark *top_parentrc, LOCKMODE lockmode,- List **appinfos)+ Index parentRTindex, RelOptInfo *parentrel)
{
+ LOCKMODE lockmode = parentrte->rellockmode;+ PlanRowMark *rootrc = NULL;
int i;
- RangeTblEntry *childrte;- Index childRTindex;- PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);+ Bitmapset *partindexes;+ Index rootParentRTindex = parentrel->inh_root_parent > 0 ?+ parentrel->inh_root_parent :+ parentRTindex;- check_stack_depth();-- /* A partitioned table should always have a partition descriptor. */- Assert(partdesc);-- Assert(parentrte->inh);+ /* If root partitioned table, lock *all* partitions in the tree. */+ if (parentRTindex == rootParentRTindex)+ (void) find_all_inheritors(parentrte->relid, lockmode, NULL);
/*
- * 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- * because parentrte already has the root partrel's updatedCols translated- * to match the attribute ordering of parentrel.+ * Initialize partitioned_child_rels to contain this RT index.+ *+ * Note that during the set_append_rel_pathlist() phase, values of the+ * indexes of partitioned relations that appear down in the tree will be+ * bubbled up into root parent's list so that when we've created Paths for+ * all the children, the root table's list will contain all such indexes.
*/
- if (!root->partColsUpdated)- root->partColsUpdated =- has_partition_attrs(parentrel, parentrte->updatedCols, NULL);+ parentrel->partitioned_child_rels = list_make1_int(parentRTindex);- /* First expand the partitioned table itself. */- expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,- top_parentrc, parentrel,- appinfos, &childrte, &childRTindex);+ /* Perform pruning. */+ partindexes = prune_append_rel_partitions(parentrel);++ /* Must expand PlannerInfo arrays before we can add children. */+ expand_planner_arrays(root, bms_num_members(partindexes));
/*
- * If the partitioned table has no partitions, treat this as the- * non-inheritance case.+ * For partitioned tables, we also store the partition RelOptInfo+ * pointers in the parent's RelOptInfo.
*/
- if (partdesc->nparts == 0)- {- parentrte->inh = false;- return;- }+ parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *+ parentrel->nparts);- for (i = 0; i < partdesc->nparts; i++)+ rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex);+ Assert(rootrc == NULL || rootrc->isParent);+ i = -1;+ while ((i = bms_next_member(partindexes, i)) >= 0)
{
- Oid childOID = partdesc->oids[i];- Relation childrel;+ Oid childOID = parentrel->part_oids[i];+ Relation newrelation;+ RelOptInfo *childrel;- /* Open rel; we already have required locks */- childrel = heap_open(childOID, NoLock);+ /* Already locked above. */+ newrelation = heap_open(childOID, NoLock);+ Assert(!RELATION_IS_OTHER_TEMP(newrelation));
/*
- * Temporary partitions belonging to other sessions should have been- * disallowed at definition, but for paranoia's sake, let's double- * check.+ * A partitioned child table with 0 children is a dummy rel, so don't+ * bother creating planner objects for it.
*/
- if (RELATION_IS_OTHER_TEMP(childrel))- elog(ERROR, "temporary relation from another session found as partition");+ if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&+ RelationGetPartitionDesc(newrelation)->nparts == 0)+ {+ heap_close(newrelation, NoLock);+ continue;+ }- expand_single_inheritance_child(root, parentrte, parentRTindex,- parentrel, top_parentrc, childrel,- appinfos, &childrte, &childRTindex);+ childrel = add_inheritance_child_rel(root, parentrte, parentRTindex,+ parentrel, rootrc, newrelation);+ Assert(childrel != NULL);+ parentrel->part_rels[i] = childrel;- /* If this child is itself partitioned, recurse */- if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)- expand_partitioned_rtentry(root, childrte, childRTindex,- childrel, top_parentrc, lockmode,- appinfos);+ /* Close child relations, but keep locks */+ heap_close(newrelation, NoLock);+ }- /* Close child relation, but keep locks */- heap_close(childrel, NoLock);+ /*+ * Add junk columns needed by the row mark if any and also add the+ * relevant expressions to the root parent's reltarget.+ */+ if (rootrc)+ {+ List *tlist = add_rowmark_junk_columns(root, rootrc);++ build_base_rel_tlists(root, tlist);
}
}
/*
- * expand_single_inheritance_child- * Build a RangeTblEntry and an AppendRelInfo, if appropriate, plus- * maybe a PlanRowMark.+ * add_inheritance_child_rel+ * Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally+ * a RelOptInfo for an inheritance child relation.
*
- * We now expand the partition hierarchy level by level, creating a- * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each- * partitioned descendant acts as a parent of its immediate partitions.- * (This is a difference from what older versions of PostgreSQL did and what- * is still done in the case of table inheritance for unpartitioned tables,- * where the hierarchy is flattened during RTE expansion.)+ * The return value is the RelOptInfo that's added.
*
* PlanRowMarks still carry the top-parent's RTI, and the top-parent's
* allMarkTypes field still accumulates values from all descendents.
*
* "parentrte" and "parentRTindex" are immediate parent's RTE and
* RTI. "top_parentrc" is top parent's PlanRowMark.
- *- * The child RangeTblEntry and its RTI are returned in "childrte_p" and- * "childRTindex_p" resp.
*/
-static void-expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,- Index parentRTindex, Relation parentrel,- PlanRowMark *top_parentrc, Relation childrel,- List **appinfos, RangeTblEntry **childrte_p,- Index *childRTindex_p)+static RelOptInfo *+add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel,+ PlanRowMark *top_parentrc, Relation childrel)
{
Query *parse = root->parse;
- Oid parentOID = RelationGetRelid(parentrel);
Oid childOID = RelationGetRelid(childrel);
RangeTblEntry *childrte;
Index childRTindex;
AppendRelInfo *appinfo;
+ RelOptInfo *childrelopt;
/*
* Build an RTE for the child, and attach to query's rangetable list. We
* copy most fields of the parent's RTE, but replace relation OID and
- * relkind, and set inh = false. Also, set requiredPerms to zero since- * all required permissions checks are done on the original RTE. Likewise,- * set the child's securityQuals to empty, because we only want to apply- * the parent's RLS conditions regardless of what RLS properties+ * relkind, and set inh appropriately. Also, set requiredPerms to zero+ * since all required permissions checks are done on the original RTE.+ * Likewise, set the child's securityQuals to empty, because we only want+ * to apply the parent's RLS conditions regardless of what RLS properties
* individual children may have. (This is an intentional choice to make
* inherited RLS work like regular permissions checks.) The parent
* securityQuals will be propagated to children along with other base
* restriction clauses, so we don't need to do it here.
*/
childrte = copyObject(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)- childrte->inh = true;- else- childrte->inh = false;+ /*+ * A partitioned child will need to be expanded as an append parent+ * itself, so set its inh to true.+ */+ childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
childrte->requiredPerms = 0;
childrte->securityQuals = NIL;
parse->rtable = lappend(parse->rtable, childrte);
childRTindex = list_length(parse->rtable);
- *childRTindex_p = childRTindex;++ /* Create an AppendRelInfo and add it to planner's global list. */+ appinfo = make_append_rel_info(parentrel, parentrte,+ childrel->rd_att,+ childOID,+ childrel->rd_rel->reltype,+ childRTindex);+ root->append_rel_list = lappend(root->append_rel_list, appinfo);
/*
- * 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.+ * Translate the column permissions bitmaps to the child's attnums (we+ * have to build the translated_vars list before we can do this). But+ * if this is the parent table, leave copyObject's result alone.+ *+ * Note: we need to do this even though the executor won't run any+ * permissions checks on the child RTE. The insertedCols/updatedCols+ * bitmaps may be examined for trigger-firing purposes.
*/
- if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)+ if (childrte->relid != parentrte->relid)
{
- appinfo = makeNode(AppendRelInfo);- appinfo->parent_relid = parentRTindex;- appinfo->child_relid = childRTindex;- appinfo->parent_reltype = parentrel->rd_rel->reltype;- appinfo->child_reltype = childrel->rd_rel->reltype;- make_inh_translation_list(parentrel, childrel, childRTindex,- &appinfo->translated_vars);- appinfo->parent_reloid = parentOID;- *appinfos = lappend(*appinfos, appinfo);-- /*- * Translate the column permissions bitmaps to the child's attnums (we- * have to build the translated_vars list before we can do this). But- * if this is the parent table, leave copyObject's result alone.- *- * Note: we need to do this even though the executor won't run any- * permissions checks on the child RTE. The insertedCols/updatedCols- * bitmaps may be examined for trigger-firing purposes.- */- if (childOID != parentOID)- {- childrte->selectedCols = translate_col_privs(parentrte->selectedCols,- appinfo->translated_vars);- childrte->insertedCols = translate_col_privs(parentrte->insertedCols,- appinfo->translated_vars);- childrte->updatedCols = translate_col_privs(parentrte->updatedCols,- appinfo->translated_vars);- }+ childrte->selectedCols = translate_col_privs(parentrte->selectedCols,+ appinfo->translated_vars);+ childrte->insertedCols = translate_col_privs(parentrte->insertedCols,+ appinfo->translated_vars);+ childrte->updatedCols = translate_col_privs(parentrte->updatedCols,+ appinfo->translated_vars);
}
/*
@@ -1867,6 +1883,24 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
root->rowMarks = lappend(root->rowMarks, childrc);
}
++ /*+ * Add the RelOptInfo. Even though we may not really scan this relation+ * for reasons such as contradictory quals, we still need need to create+ * one, because for every RTE in the query's range table, there must be an+ * accompanying RelOptInfo.+ */++ /* First, store the RTE and appinfos into planner arrays. */+ 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;++ childrelopt = build_append_child_rel(root, parentrel, childRTindex);+ Assert(childrelopt != NULL);++ return childrelopt;
}
/*
@@ -1877,14 +1911,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
* For paranoia's sake, we match type/collation as well as attribute name.
*/
static void
-make_inh_translation_list(Relation oldrelation, Relation newrelation,- Index newvarno,- List **translated_vars)+make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc,+ Oid from_rel, Oid to_rel,+ Index newvarno, List **translated_vars)
{
List *vars = NIL;
- TupleDesc old_tupdesc = RelationGetDescr(oldrelation);- TupleDesc new_tupdesc = RelationGetDescr(newrelation);- Oid new_relid = RelationGetRelid(newrelation);
int oldnatts = old_tupdesc->natts;
int newnatts = new_tupdesc->natts;
int old_attno;
@@ -1914,7 +1945,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
* When we are generating the "translation list" for the parent table
* of an inheritance set, no need to search for matches.
*/
- if (oldrelation == newrelation)+ if (from_rel == to_rel)
{
vars = lappend(vars, makeVar(newvarno,
(AttrNumber) (old_attno + 1),
@@ -1940,10 +1971,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
{
HeapTuple newtup;
- newtup = SearchSysCacheAttName(new_relid, attname);+ newtup = SearchSysCacheAttName(to_rel, attname);
if (!newtup)
elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
- attname, RelationGetRelationName(newrelation));+ attname, get_rel_name(to_rel));
new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
ReleaseSysCache(newtup);
@@ -1953,10 +1984,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
/* Found it, check type and collation match */
if (atttypid != att->atttypid || atttypmod != att->atttypmod)
elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
- attname, RelationGetRelationName(newrelation));+ attname, get_rel_name(to_rel));
if (attcollation != att->attcollation)
elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
- attname, RelationGetRelationName(newrelation));+ attname, get_rel_name(to_rel));
vars = lappend(vars, makeVar(newvarno,
(AttrNumber) (new_attno + 1),
@@ -2023,6 +2054,255 @@ translate_col_privs(const Bitmapset *parent_privs,
}
/*
+ * build_append_child_rel+ * Build a RelOptInfo for child relation of an append rel+ *+ * After creating the RelOptInfo for the given child RT index, it goes on to+ * initialize some of its fields base on the parent RelOptInfo.+ *+ * If the quals in baserestrictinfo turns out to be self-contradictory, the+ * RelOptInfo is marked dummy before returning.+ */+static RelOptInfo *+build_append_child_rel(PlannerInfo *root,+ RelOptInfo *parent,+ Index childRTindex)+{+ RelOptInfo *childrel;+ RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];+ AppendRelInfo *appinfo = root->append_rel_array[childRTindex];+ List *childquals;+ ListCell *lc;+ bool have_const_false_cq;+ Index cq_min_security;++ /* Build the RelOptInfo. */+ childrel = build_simple_rel(root, childRTindex, parent);++ /*+ * Propagate lateral_relids and lateral_referencers from appendrel+ * parent rels to their child rels. We intentionally give each child rel+ * the same minimum parameterization, even though it's quite possible that+ * some don't reference all the lateral rels. This is because any append+ * path for the parent will have to have the same parameterization for+ * every child anyway, and there's no value in forcing extra+ * reparameterize_path() calls. Similarly, a lateral reference to the+ * parent prevents use of otherwise-movable join rels for each child.+ */+ childrel->direct_lateral_relids = parent->direct_lateral_relids;+ childrel->lateral_relids = parent->lateral_relids;+ childrel->lateral_referencers = parent->lateral_referencers;++ /*+ * We have to copy the parent's quals to the child, with appropriate+ * substitution of variables. However, only the baserestrictinfo+ * quals are needed before we can check for constraint exclusion; so+ * do that first and then check to see if we can disregard this child.+ *+ * The child rel's targetlist might contain non-Var expressions, which+ * means that substitution into the quals could produce opportunities+ * for const-simplification, and perhaps even pseudoconstant quals.+ * Therefore, transform each RestrictInfo separately to see if it+ * reduces to a constant or pseudoconstant. (We must process them+ * separately to keep track of the security level of each qual.)+ */+ childquals = false;+ cq_min_security = UINT_MAX;+ have_const_false_cq = false;+ foreach(lc, parent->baserestrictinfo)+ {+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);+ Node *childqual = (Node *) rinfo->clause;+ ListCell *lc2;++ Assert(IsA(rinfo, RestrictInfo));+ childqual = adjust_appendrel_attrs(root, childqual,+ 1, &appinfo);+ childqual = eval_const_expressions(root, childqual);+ /* check for flat-out constant */+ if (childqual && IsA(childqual, Const))+ {+ if (((Const *) childqual)->constisnull ||+ !DatumGetBool(((Const *) childqual)->constvalue))+ {+ /* Restriction reduces to constant FALSE or NULL */+ have_const_false_cq = true;+ break;+ }+ /* Restriction reduces to constant TRUE, so drop it */+ continue;+ }+ /* might have gotten an AND clause, if so flatten it */+ foreach(lc2, make_ands_implicit((Expr *) childqual))+ {+ Node *onecq = (Node *) lfirst(lc2);+ bool pseudoconstant;++ /* check for pseudoconstant (no Vars or volatile functions) */+ pseudoconstant =+ !contain_vars_of_level(onecq, 0) &&+ !contain_volatile_functions(onecq);+ if (pseudoconstant)+ {+ /* tell createplan.c to check for gating quals */+ root->hasPseudoConstantQuals = true;+ }+ /* reconstitute RestrictInfo with appropriate properties */+ childquals = lappend(childquals,+ make_restrictinfo((Expr *) onecq,+ rinfo->is_pushed_down,+ rinfo->outerjoin_delayed,+ pseudoconstant,+ rinfo->security_level,+ NULL, NULL, NULL));+ cq_min_security = Min(cq_min_security, rinfo->security_level);+ }+ }++ /*+ * In addition to the quals inherited from the parent, we might+ * have securityQuals associated with this particular child node.+ * (Currently this can only happen in appendrels originating from+ * UNION ALL; inheritance child tables don't have their own+ * securityQuals.) Pull any such securityQuals up into the+ * baserestrictinfo for the child. This is similar to+ * process_security_barrier_quals() for the parent rel, except+ * that we can't make any general deductions from such quals,+ * since they don't hold for the whole appendrel.+ */+ if (childRTE->securityQuals)+ {+ Index security_level = 0;++ foreach(lc, childRTE->securityQuals)+ {+ List *qualset = (List *) lfirst(lc);+ ListCell *lc2;++ foreach(lc2, qualset)+ {+ Expr *qual = (Expr *) lfirst(lc2);++ /*+ * not likely that we'd see constants here, so no+ * check+ */+ childquals = lappend(childquals,+ make_restrictinfo(qual,+ true,+ false,+ false,+ security_level,+ NULL, NULL,+ NULL));+ cq_min_security = Min(cq_min_security, security_level);+ }+ security_level++;+ }+ Assert(security_level <= root->qual_security_level);+ }++ /* Set child's version of baserestrictinfo. */+ childrel->baserestrictinfo = childquals;+ childrel->baserestrict_min_security = cq_min_security;++ if (have_const_false_cq)+ {+ /*+ * Some restriction clause reduced to constant FALSE or NULL after+ * substitution, so this child need not be scanned.+ */+ set_dummy_rel_pathlist(childrel);+ }++ return childrel;+}++/*+ * add_rowmark_junk_columns+ * Add necessary junk columns for rowmarked inheritance parent rel.+ *+ * These values are needed for locking of rels selected FOR UPDATE/SHARE, and+ * to do EvalPlanQual rechecking. See comments for PlanRowMark in+ * plannodes.h.+ */+static List *+add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc)+{+ List *tlist = root->processed_tlist;+ Var *var;+ char resname[32];+ TargetEntry *tle;++ if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))+ {+ /* Need to fetch TID */+ var = makeVar(rc->rti,+ SelfItemPointerAttributeNumber,+ TIDOID,+ -1,+ InvalidOid,+ 0);+ snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);+ }+ if (rc->allMarkTypes & (1 << ROW_MARK_COPY))+ {+ /* Need the whole row as a junk var */+ var = makeWholeRowVar(root->simple_rte_array[rc->rti],+ rc->rti,+ 0,+ false);+ snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);+ }++ /* For inheritance cases, always fetch the tableoid too. */+ var = makeVar(rc->rti,+ TableOidAttributeNumber,+ OIDOID,+ -1,+ InvalidOid,+ 0);+ snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);++ return tlist;+}++AppendRelInfo *+make_append_rel_info(RelOptInfo *parentrel, RangeTblEntry *parentrte,+ TupleDesc childdesc, Oid childoid, Oid childreltype,+ Index childRTindex)+{+ AppendRelInfo *appinfo = makeNode(AppendRelInfo);++ appinfo->parent_relid = parentrel->relid;+ appinfo->child_relid = childRTindex;+ appinfo->parent_reltype = parentrel->reltype;+ appinfo->child_reltype = childreltype;+ make_inh_translation_list(parentrel->tupdesc, childdesc,+ parentrte->relid, childoid,+ childRTindex,+ &appinfo->translated_vars);+ appinfo->parent_reloid = parentrte->relid;++ return appinfo;+}++/*
* adjust_appendrel_attrs
* Copy the specified query or expression and translate Vars referring to a
* parent rel to refer to the corresponding child rel instead. We also
diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefileindex c54d0a690d..da9ccf32b4 100644--- a/src/backend/optimizer/util/Makefile+++ b/src/backend/optimizer/util/Makefile@@ -13,6 +13,6 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = clauses.o joininfo.o orclauses.o pathnode.o placeholder.o \
- plancat.o predtest.o relnode.o restrictinfo.o tlist.o var.o+ plancat.o predtest.o relnode.o restrictinfo.o tlist.o var.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.cindex ed0953f9e1..ce8a8fb78e 100644--- a/src/backend/optimizer/util/plancat.c+++ b/src/backend/optimizer/util/plancat.c@@ -106,7 +106,7 @@ static void set_baserel_partition_key_exprs(Relation relation,
*/
void
get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
- RelOptInfo *rel)+ Bitmapset *updatedCols, RelOptInfo *rel)
{
Index varno = rel->relid;
Relation relation;
@@ -444,11 +444,32 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
get_relation_foreign_keys(root, rel, relation, inhparent);
/*
- * Collect info about relation's partitioning scheme, if any. Only- * inheritance parents may be partitioned.+ * Collect some additional information for inheritance parents.
*/
- if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)- set_relation_partition_info(root, rel, relation);+ if (inhparent)+ {+ /*+ * We'll need the TupleDesc when initializing the child relation.+ * A copy is being made because concurrent changes might drop+ * the relcache entry. That's possible because ALTER TABLE+ * child_table NO INHERIT parent_table only requires an+ * AccessShareLock on parent_table.+ */+ rel->tupdesc = CreateTupleDescCopy(RelationGetDescr(relation));+ rel->reltype = RelationGetForm(relation)->reltype;++ /*+ * If partitioned, also save the information of partitioning scheme,+ * and whether the query updates any of the partition key columns.+ */+ if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)+ {+ set_relation_partition_info(root, rel, relation);+ if (!root->partColsUpdated)+ root->partColsUpdated =+ has_partition_attrs(relation, updatedCols, NULL);+ }+ }
heap_close(relation, NoLock);
@@ -1854,16 +1875,20 @@ set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
Relation relation)
{
PartitionDesc partdesc;
- PartitionKey partkey;
Assert(relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
partdesc = RelationGetPartitionDesc(relation);
- partkey = RelationGetPartitionKey(relation);
rel->part_scheme = find_partition_scheme(root, relation);
Assert(partdesc != NULL && rel->part_scheme != NULL);
- rel->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
rel->nparts = partdesc->nparts;
++ /*+ * Since we must've taken a lock on the table, it's okay to simply copy+ * the pointers to relcache data here.+ */+ rel->part_oids = partdesc->oids;+ rel->boundinfo = partdesc->boundinfo;
set_baserel_partition_key_exprs(relation, rel);
rel->partition_qual = RelationGetPartitionQual(relation);
}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.cindex 29ba19349f..c23db9d78d 100644--- a/src/backend/optimizer/util/relnode.c+++ b/src/backend/optimizer/util/relnode.c@@ -130,6 +130,37 @@ setup_append_rel_array(PlannerInfo *root)
}
}
+/* Expand the PlannerInfo arrays by add_size members and zero-init it. */+void+expand_planner_arrays(PlannerInfo *root, int add_size)+{+ int new_size = root->simple_rel_array_size + add_size;++ root->simple_rte_array = (RangeTblEntry **)+ repalloc(root->simple_rte_array,+ sizeof(RangeTblEntry *) * new_size);+ root->simple_rel_array = (RelOptInfo **)+ repalloc(root->simple_rel_array,+ sizeof(RelOptInfo *) * new_size);+ if (root->append_rel_array)+ root->append_rel_array = (AppendRelInfo **)+ repalloc(root->append_rel_array,+ sizeof(AppendRelInfo *) * new_size);+ else+ root->append_rel_array = (AppendRelInfo **)+ palloc0(sizeof(AppendRelInfo *) *+ new_size);++ /* Set the contents of just allocated memory to 0. */+ MemSet(root->simple_rte_array + root->simple_rel_array_size,+ 0, sizeof(RangeTblEntry *) * add_size);+ MemSet(root->simple_rel_array + root->simple_rel_array_size,+ 0, sizeof(RelOptInfo *) * add_size);+ MemSet(root->append_rel_array + root->simple_rel_array_size,+ 0, sizeof(AppendRelInfo *) * add_size);+ root->simple_rel_array_size = new_size;+}+
/*
* build_simple_rel
* Construct a new RelOptInfo for a base relation or 'other' relation.
@@ -238,7 +269,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
{
case RTE_RELATION:
/* Table --- retrieve statistics from the system catalogs */
- get_relation_info(root, rte->relid, rte->inh, rel);+ get_relation_info(root, rte->relid, rte->inh, rte->updatedCols,+ rel);
break;
case RTE_SUBQUERY:
case RTE_FUNCTION:
@@ -279,52 +311,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
root->qual_security_level = Max(root->qual_security_level,
list_length(rte->securityQuals));
- /*- * If this rel is an appendrel parent, recurse to build "other rel"- * RelOptInfos for its children. They are "other rels" because they are- * not in the main join tree, but we will need RelOptInfos to plan access- * to them.- */- if (rte->inh)- {- ListCell *l;- int nparts = rel->nparts;- int cnt_parts = 0;-- if (nparts > 0)- rel->part_rels = (RelOptInfo **)- palloc(sizeof(RelOptInfo *) * nparts);-- foreach(l, root->append_rel_list)- {- AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);- RelOptInfo *childrel;-- /* append_rel_list contains all append rels; ignore others */- if (appinfo->parent_relid != relid)- continue;-- childrel = build_simple_rel(root, appinfo->child_relid,- rel);-- /* Nothing more to do for an unpartitioned table. */- if (!rel->part_scheme)- continue;-- /*- * The order of partition OIDs in append_rel_list is the same as- * the order in the PartitionDesc, so the order of part_rels will- * also match the PartitionDesc. See expand_partitioned_rtentry.- */- Assert(cnt_parts < nparts);- rel->part_rels[cnt_parts] = childrel;- cnt_parts++;- }-- /* We should have seen all the child partitions. */- Assert(cnt_parts == nparts);- }-
return rel;
}
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.cindex d6ca03de4a..bf17ae22ba 100644--- a/src/backend/partitioning/partprune.c+++ b/src/backend/partitioning/partprune.c@@ -45,6 +45,7 @@
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/planner.h"
#include "optimizer/predtest.h"
@@ -436,17 +437,23 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
* is, not pruned already).
*/
subplan_map = (int *) palloc(nparts * sizeof(int));
+ memset(subplan_map, -1, nparts * sizeof(int));
subpart_map = (int *) palloc(nparts * sizeof(int));
+ memset(subpart_map, -1, nparts * sizeof(int));
present_parts = NULL;
for (i = 0; i < nparts; i++)
{
RelOptInfo *partrel = subpart->part_rels[i];
- int subplanidx = relid_subplan_map[partrel->relid] - 1;- int subpartidx = relid_subpart_map[partrel->relid] - 1;+ int subplanidx;+ int subpartidx;- subplan_map[i] = subplanidx;- subpart_map[i] = subpartidx;+ /* Skip processing pruned partitions. */+ if (partrel == NULL)+ continue;++ subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;+ subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
if (subplanidx >= 0)
{
present_parts = bms_add_member(present_parts, i);
@@ -540,23 +547,20 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory)
/*
* prune_append_rel_partitions
- * Returns RT indexes of the minimum set of child partitions which must- * be scanned to satisfy rel's baserestrictinfo quals.+ * Returns indexes into rel->part_rels of the minimum set of child+ * partitions which must be scanned to satisfy rel's baserestrictinfo+ * quals.
*
* Callers must ensure that 'rel' is a partitioned table.
*/
-Relids+Bitmapset *
prune_append_rel_partitions(RelOptInfo *rel)
{
- Relids result;
List *clauses = rel->baserestrictinfo;
List *pruning_steps;
bool contradictory;
PartitionPruneContext context;
- Bitmapset *partindexes;- int i;- Assert(clauses != NIL);
Assert(rel->part_scheme != NULL);
/* If there are no partitions, return the empty set */
@@ -564,6 +568,13 @@ prune_append_rel_partitions(RelOptInfo *rel)
return NULL;
/*
+ * If pruning is disabled or if there are no clauses to prune with,+ * return all partitions.+ */+ if (!enable_partition_pruning || clauses == NIL)+ return bms_add_range(NULL, 0, rel->nparts - 1);++ /*
* Process clauses. If the clauses are found to be contradictory, we can
* return the empty set.
*/
@@ -590,15 +601,7 @@ prune_append_rel_partitions(RelOptInfo *rel)
context.evalexecparams = false;
/* Actual pruning happens here. */
- partindexes = get_matching_partitions(&context, pruning_steps);-- /* Add selected partitions' RT indexes to result. */- i = -1;- result = NULL;- while ((i = bms_next_member(partindexes, i)) >= 0)- result = bms_add_member(result, rel->part_rels[i]->relid);-- return result;+ return get_matching_partitions(&context, pruning_steps);
}
/*
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.hindex 29339d6c72..6b614e4794 100644--- a/src/include/nodes/relation.h+++ b/src/include/nodes/relation.h@@ -15,6 +15,7 @@
#define RELATION_H
#include "access/sdir.h"
+#include "access/tupdesc.h"
#include "fmgr.h"
#include "lib/stringinfo.h"
#include "nodes/params.h"
@@ -710,6 +711,7 @@ typedef struct RelOptInfo
int nparts; /* number of partitions */
struct PartitionBoundInfoData *boundinfo; /* Partition bounds */
List *partition_qual; /* partition constraint */
+ Oid *part_oids; /* partition OIDs */
struct RelOptInfo **part_rels; /* Array of RelOptInfos of partitions,
* stored in the same order of bounds */
List **partexprs; /* Non-nullable partition key expressions. */
@@ -719,6 +721,12 @@ typedef struct RelOptInfo
Index inh_root_parent; /* For otherrels, this is the RT index of
* inheritance table mentioned in the query
* from which this relation originated */
+ /*+ * Set only if this is an inheritance parent relation. This information+ * is needed when initializing the planning info for children.+ */+ TupleDesc tupdesc; /* A "copy" of the table's tuple desriptor */+ Oid reltype; /* Table's reltype */
} RelOptInfo;
/*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.hindex 81abcf53a8..b1baa3117a 100644--- a/src/include/optimizer/pathnode.h+++ b/src/include/optimizer/pathnode.h@@ -261,6 +261,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 RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.hindex 7d53cbbb87..edaf2a3b4f 100644--- a/src/include/optimizer/plancat.h+++ b/src/include/optimizer/plancat.h@@ -26,7 +26,7 @@ extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook;
extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
- bool inhparent, RelOptInfo *rel);+ bool inhparent, Bitmapset *updatedCols, RelOptInfo *rel);
extern List *infer_arbiter_indexes(PlannerInfo *root);
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.hindex 38608770a2..e8e21e2ed5 100644--- a/src/include/optimizer/prep.h+++ b/src/include/optimizer/prep.h@@ -38,7 +38,7 @@ extern Expr *canonicalize_qual(Expr *qual, bool is_check);
/*
* prototypes for preptlist.c
*/
-extern List *preprocess_targetlist(PlannerInfo *root);+extern List *preprocess_targetlist(PlannerInfo *root, bool inheritance_update);
extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
@@ -46,12 +46,16 @@ extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
* prototypes for prepunion.c
*/
extern RelOptInfo *plan_set_operations(PlannerInfo *root);
--extern void expand_inherited_tables(PlannerInfo *root);-+extern void expand_append_rel(PlannerInfo *root,+ RelOptInfo *rel,+ RangeTblEntry *rte,+ Index rti);
extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
int nappinfos, AppendRelInfo **appinfos);
-+extern AppendRelInfo *make_append_rel_info(RelOptInfo *parentrel,+ RangeTblEntry *parentrte,+ TupleDesc childdesc, Oid childoid, Oid childreltype,+ Index childRTindex);
extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
Relids child_relids,
Relids top_parent_relids);
@@ -64,5 +68,4 @@ extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
Relids left_relids, Relids right_relids);
extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
Relids child_relids, Relids top_parent_relids);
-
#endif /* PREP_H */
diff --git a/src/include/partitioning/partprune.h b/src/include/partitioning/partprune.hindex e07aaaf798..ac0a979010 100644--- a/src/include/partitioning/partprune.h+++ b/src/include/partitioning/partprune.h@@ -76,7 +76,7 @@ extern PartitionPruneInfo *make_partition_pruneinfo(PlannerInfo *root,
List *subpaths,
List *partitioned_rels,
List *prunequal);
-extern Relids prune_append_rel_partitions(RelOptInfo *rel);+extern Bitmapset *prune_append_rel_partitions(RelOptInfo *rel);
extern Bitmapset *get_matching_partitions(PartitionPruneContext *context,
List *pruning_steps);
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.outindex 1f5378080d..73f684f992 100644--- a/src/test/regress/expected/join.out+++ b/src/test/regress/expected/join.out@@ -5547,29 +5547,29 @@ select t1.b, ss.phv from join_ut1 t1 left join lateral
(select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv
from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss
on t1.a = ss.t2a order by t1.a;
- QUERY PLAN -------------------------------------------------------------------+ QUERY PLAN +--------------------------------------------------------------------
Sort
- Output: t1.b, (LEAST(t1.a, t2.a, t3.a)), t1.a+ Output: t1.b, (LEAST(t1.a, t2_1.a, t3.a)), t1.a
Sort Key: t1.a
-> Nested Loop Left Join
- Output: t1.b, (LEAST(t1.a, t2.a, t3.a)), t1.a+ Output: t1.b, (LEAST(t1.a, t2_1.a, t3.a)), t1.a
-> Seq Scan on public.join_ut1 t1
Output: t1.a, t1.b, t1.c
-> Hash Join
- Output: t2.a, LEAST(t1.a, t2.a, t3.a)- Hash Cond: (t3.b = t2.a)+ Output: t2_1.a, LEAST(t1.a, t2_1.a, t3.a)+ Hash Cond: (t3.b = t2_1.a)
-> Seq Scan on public.join_ut1 t3
Output: t3.a, t3.b, t3.c
-> Hash
- Output: t2.a+ Output: t2_1.a
-> Append
- -> Seq Scan on public.join_pt1p1p1 t2- Output: t2.a- Filter: (t1.a = t2.a)- -> Seq Scan on public.join_pt1p2 t2_1+ -> Seq Scan on public.join_pt1p1p1 t2_1
Output: t2_1.a
Filter: (t1.a = t2_1.a)
+ -> Seq Scan on public.join_pt1p2 t2+ Output: t2.a+ Filter: (t1.a = t2.a)
(21 rows)
select t1.b, ss.phv from join_ut1 t1 left join lateral
diff --git a/src/test/regress/expected/partition_aggregate.out b/src/test/regress/expected/partition_aggregate.outindex 6bc106831e..1450cef057 100644--- a/src/test/regress/expected/partition_aggregate.out+++ b/src/test/regress/expected/partition_aggregate.out@@ -144,7 +144,7 @@ SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c;
QUERY PLAN
--------------------------------
HashAggregate
- Group Key: pagg_tab.c+ Group Key: c
-> Result
One-Time Filter: false
(4 rows)
@@ -159,7 +159,7 @@ SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c;
QUERY PLAN
--------------------------------
GroupAggregate
- Group Key: pagg_tab.c+ Group Key: c
-> Result
One-Time Filter: false
(4 rows)
--
2.11.0
[text/plain] v5-0004-Move-append-expansion-code-into-its-own-file.patch (108.9K, ../3be67028-a00a-502c-199a-da00eec8fb6e@lab.ntt.co.jp/5-v5-0004-Move-append-expansion-code-into-its-own-file.patch)
download | inline diff:
From 68c565448b11e4c1294411b1841a6d527bdb9726 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 7 Nov 2018 16:51:31 +0900
Subject: [PATCH v5 4/6] Move append expansion code into its own file
This commit moves expand_append_rel and underlings to
optimizer/utils/append.c.
Also, all of the AppendRelInfo based
expression manipulation routines are moved to
optimizer/utils/appendinfo.c.
This commit only moves the code and contains no functional changes.
---
src/backend/optimizer/path/allpaths.c | 2 +
src/backend/optimizer/path/equivclass.c | 1 +
src/backend/optimizer/path/joinrels.c | 1 +
src/backend/optimizer/plan/planner.c | 1 +
src/backend/optimizer/prep/prepunion.c | 1516 -------------------------------
src/backend/optimizer/util/Makefile | 5 +-
src/backend/optimizer/util/append.c | 719 +++++++++++++++
src/backend/optimizer/util/appendinfo.c | 851 +++++++++++++++++
src/backend/optimizer/util/pathnode.c | 1 +
src/backend/optimizer/util/relnode.c | 1 +
src/backend/partitioning/partprune.c | 1 +
src/include/optimizer/append.h | 28 +
src/include/optimizer/appendinfo.h | 43 +
src/include/optimizer/prep.h | 21 -
14 files changed, 1652 insertions(+), 1539 deletions(-)
create mode 100644 src/backend/optimizer/util/append.c
create mode 100644 src/backend/optimizer/util/appendinfo.c
create mode 100644 src/include/optimizer/append.h
create mode 100644 src/include/optimizer/appendinfo.h
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.cindex 6ee800d84a..0d051681e6 100644--- a/src/backend/optimizer/path/allpaths.c+++ b/src/backend/optimizer/path/allpaths.c@@ -30,6 +30,8 @@
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
+#include "optimizer/append.h"+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/geqo.h"
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.cindex b22b36ec0e..5ace28eab0 100644--- a/src/backend/optimizer/path/equivclass.c+++ b/src/backend/optimizer/path/equivclass.c@@ -22,6 +22,7 @@
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.cindex da0831de4e..6e321ec9e7 100644--- a/src/backend/optimizer/path/joinrels.c+++ b/src/backend/optimizer/path/joinrels.c@@ -15,6 +15,7 @@
#include "postgres.h"
#include "miscadmin.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.cindex 84746a9bc9..216cadfba9 100644--- a/src/backend/optimizer/plan/planner.c+++ b/src/backend/optimizer/plan/planner.c@@ -39,6 +39,7 @@
#include "nodes/print.h"
#endif
#include "nodes/relation.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.cindex c6817757e7..70f37c593f 100644--- a/src/backend/optimizer/prep/prepunion.c+++ b/src/backend/optimizer/prep/prepunion.c@@ -12,11 +12,6 @@
* case, but most of the heavy lifting for that is done elsewhere,
* notably in prepjointree.c and allpaths.c.
*
- * There is also some code here to support planning of queries that use- * inheritance (SELECT FROM foo*). Inheritance trees are converted into- * append relations, and thenceforth share code with the UNION ALL case.- *- *
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -39,32 +34,21 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
-#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
-#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
-#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parsetree.h"
-#include "partitioning/partprune.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
#include "utils/syscache.h"
-typedef struct-{- PlannerInfo *root;- int nappinfos;- AppendRelInfo **appinfos;-} adjust_appendrel_attrs_context;-
static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
List *colTypes, List *colCollations,
bool junkOK,
@@ -103,31 +87,6 @@ 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,- Index rti, RelOptInfo *rel);-static void expand_partitioned_rtentry(PlannerInfo *root,- RangeTblEntry *parentrte,- Index parentRTindex, RelOptInfo *parentrel);-static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root,- RangeTblEntry *parentrte,- Index parentRTindex, RelOptInfo *parentrel,- PlanRowMark *top_parentrc, Relation childrel);-static void make_inh_translation_list(TupleDesc old_tupdesc,- TupleDesc new_tupdesc,- Oid from_rel, Oid to_rel,- Index newvarno, List **translated_vars);-static RelOptInfo *build_append_child_rel(PlannerInfo *root,- RelOptInfo *parent,- Index childRTindex);-static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc);-static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,- List *translated_vars);-static Node *adjust_appendrel_attrs_mutator(Node *node,- adjust_appendrel_attrs_context *context);-static Relids adjust_child_relids(Relids relids, int nappinfos,- AppendRelInfo **appinfos);-static List *adjust_inherited_tlist(List *tlist,- AppendRelInfo *context);
/*
@@ -1464,1478 +1423,3 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Assert(lg == NULL);
return grouplist;
}
---/*- * expand_append_rel- * This initializes RelOptInfos for an appendrel's child relations.- *- * 'rel' is the appendrel parent, whose range table entry ('rte') has been- * marked to require adding children. An appendrel parent could either- * be a subquery (if we flattened UNION ALL query) or a table that's known- * to have inheritance children. The latter consists of both regular- * inheritance parents and partitioned tables.- *- * For a subquery parent, there is not much to be done here because the- * children's RTEs are already present in the query, so we just initialize- * RelOptInfos for them. Also, the AppendRelInfos for child subqueries- * have already been added.- *- * For tables, we need to add the children to the range table and initialize- * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them. For- * a partitioned parent, we only add the children remaining after pruning.- * For regular inheritance parents, we find the children using- * find_all_inheritors and add all of them.- *- * If it turns out that there are no children, then we set rte->inh to false- * to let the caller know that only the parent table needs to be scanned. The- * caller can accordingly switch to a non-Append path. For a partitioned- * parent, that means an empty relation because parents themselves contain no- * data.- *- * For the regular inheritance case, the parent also gets another RTE with- * inh = false to represent it as an appendrel child. The original RTE is- * considered to represent the whole inheritance set.- */-void-expand_append_rel(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,- Index rti)-{- Assert(rte->inh);- /* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */- Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY);-- /*- * UNION ALL children already got RTEs and AppendRelInfos, so just build- * RelOptInfos and return.- *- * It might be a bit odd that this code is in this, because there is- * nothing to expand really.- */- if (rte->rtekind == RTE_SUBQUERY)- {- ListCell *l;-- /*- * We don't need to use expand_planner_arrays in this case, because- * no new child RTEs are created. setup_simple_rel_arrays() and- * setup_append_rel_array would've considered these child RTEs when- * allocating space for various arrays.- */- foreach(l, root->append_rel_list)- {- AppendRelInfo *appinfo = lfirst(l);- Index childRTindex = appinfo->child_relid;-- if (appinfo->parent_relid != rti)- continue;-- Assert(childRTindex < root->simple_rel_array_size);- Assert(root->simple_rte_array[childRTindex] != NULL);-- /*- * We set the correct value of baserestricinfo and- * baserestrict_min_security below.- */- root->simple_rel_array[childRTindex] =- build_append_child_rel(root, rel, appinfo->child_relid);- }- }- else- {- Assert(rte->rtekind == RTE_RELATION);- Assert(has_subclass(rte->relid));-- /*- * The rewriter should already have obtained an appropriate lock on- * each relation named in the query. However, for each child relation- * we add to the query, we must obtain an appropriate lock, because- * this will be the first use of those relations in the- * parse/rewrite/plan pipeline. Child rels should use the same- * lockmode as their parent.- */- Assert(rte->rellockmode != NoLock);-- if (rte->relkind == RELKIND_PARTITIONED_TABLE)- expand_partitioned_rtentry(root, rte, rti, rel);- else- expand_inherited_rtentry(root, rte, rti, rel);- }-}--/*- * expand_inherited_rtentry- * Add entries for all the child tables to the query's rangetable, and- * build AppendRelInfo nodes for all the child tables and add them to- * root->append_rel_list.- *- * Note that the original RTE is considered to represent the whole- * inheritance set. The first of the generated RTEs is an RTE for the same- * table, but with inh = false, to represent the parent table in its role- * as a simple member of the inheritance set.- *- * A childless table is never considered to be an inheritance set. For- * regular inheritance, a parent RTE must always have at least two associated- * AppendRelInfos: one corresponding to the parent table as a simple member of- * inheritance set and one or more corresponding to the actual children.- */-static void-expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti,- RelOptInfo *rel)-{- Oid parentOID;- PlanRowMark *oldrc;- LOCKMODE lockmode = rte->rellockmode;- List *inhOIDs;- ListCell *l;- int num_children;- int num_children_added = 0;-- Assert(rte->rtekind == RTE_RELATION);- Assert(lockmode != NoLock);- parentOID = rte->relid;-- /* Scan for all members of inheritance set, acquire needed locks */- inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);-- /*- * Check that there's at least one descendant, else treat as no-child- * case. This could happen despite above has_subclass() check, if table- * once had a child but no longer does.- */- num_children = list_length(inhOIDs);- if (num_children < 2)- {- /* Clear flag before returning */- rte->inh = false;- return;- }-- /*- * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks- * should've set isParent = true. We'll generate a new PlanRowMark for- * each child.- */- oldrc = get_plan_rowmark(root->rowMarks, rti);- Assert(oldrc == NULL || oldrc->isParent);-- /*- * Must expand PlannerInfo arrays by num_children before we can add- * children.- */- expand_planner_arrays(root, num_children);-- foreach(l, inhOIDs)- {- Oid childOID = lfirst_oid(l);- Relation newrelation;- RelOptInfo *childrel;-- /* Already locked above. */- newrelation = heap_open(childOID, NoLock);-- /*- * It is possible that the parent table has children that are temp- * tables of other backends. We cannot safely access such tables- * (because of buffering issues), and the best thing to do seems- * to be to silently ignore them.- */- if (RELATION_IS_OTHER_TEMP(newrelation))- {- heap_close(newrelation, lockmode);- continue;- }-- childrel = add_inheritance_child_rel(root, rte, rti, rel, oldrc,- newrelation);- Assert(childrel != NULL);-- /* Close child relations, but keep locks */- heap_close(newrelation, NoLock);- num_children_added++;- }-- /*- * If all children, including the parent (as child rel), were- * excluded, mark the parent rel as empty. If all the children were temp- * tables, pretend it's a non-inheritance situation; we don't need Append- * node in that case. The duplicate RTE we added for the parent table is- * harmless, so we don't bother to get rid of it; ditto for the useless- * PlanRowMark node.- */- if (num_children_added == 0)- mark_dummy_rel(rel);- else if (num_children_added == 1)- rte->inh = false;-- /*- * Add junk columns needed by the row mark if any and also add the- * relevant expressions to the root parent's reltarget.- */- if (oldrc)- {- List *tlist = add_rowmark_junk_columns(root, oldrc);-- build_base_rel_tlists(root, tlist);- }-}--/*- * expand_partitioned_rtentry- * Prunes unnecessary partitions of a partitioned table and adds- * remaining ones to the Query and the PlannerInfo- *- * Partitions are added to the query in order in which they are found in- * the parent's PartitionDesc.- *- * Note: even though only the unpruned partitions will be added to the- * resulting plan, this still locks *all* partitions via find_all_inheritors- * in order to avoid partitions being locked in a different order than other- * places in the backend that may lock partitions.- */-static void-expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,- Index parentRTindex, RelOptInfo *parentrel)-{- LOCKMODE lockmode = parentrte->rellockmode;- PlanRowMark *rootrc = NULL;- int i;- Bitmapset *partindexes;- Index rootParentRTindex = parentrel->inh_root_parent > 0 ?- parentrel->inh_root_parent :- parentRTindex;-- /* If root partitioned table, lock *all* partitions in the tree. */- if (parentRTindex == rootParentRTindex)- (void) find_all_inheritors(parentrte->relid, lockmode, NULL);-- /*- * Initialize partitioned_child_rels to contain this RT index.- *- * Note that during the set_append_rel_pathlist() phase, values of the- * indexes of partitioned relations that appear down in the tree will be- * bubbled up into root parent's list so that when we've created Paths for- * all the children, the root table's list will contain all such indexes.- */- parentrel->partitioned_child_rels = list_make1_int(parentRTindex);-- /* Perform pruning. */- partindexes = prune_append_rel_partitions(parentrel);-- /* Must expand PlannerInfo arrays before we can add children. */- expand_planner_arrays(root, bms_num_members(partindexes));-- /*- * For partitioned tables, we also store the partition RelOptInfo- * pointers in the parent's RelOptInfo.- */- parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *- parentrel->nparts);-- rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex);- Assert(rootrc == NULL || rootrc->isParent);- i = -1;- while ((i = bms_next_member(partindexes, i)) >= 0)- {- Oid childOID = parentrel->part_oids[i];- Relation newrelation;- RelOptInfo *childrel;-- /* Already locked above. */- newrelation = heap_open(childOID, NoLock);- Assert(!RELATION_IS_OTHER_TEMP(newrelation));-- /*- * A partitioned child table with 0 children is a dummy rel, so don't- * bother creating planner objects for it.- */- if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&- RelationGetPartitionDesc(newrelation)->nparts == 0)- {- heap_close(newrelation, NoLock);- continue;- }-- childrel = add_inheritance_child_rel(root, parentrte, parentRTindex,- parentrel, rootrc, newrelation);- Assert(childrel != NULL);- parentrel->part_rels[i] = childrel;-- /* Close child relations, but keep locks */- heap_close(newrelation, NoLock);- }-- /*- * Add junk columns needed by the row mark if any and also add the- * relevant expressions to the root parent's reltarget.- */- if (rootrc)- {- List *tlist = add_rowmark_junk_columns(root, rootrc);-- build_base_rel_tlists(root, tlist);- }-}--/*- * add_inheritance_child_rel- * Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally- * a RelOptInfo for an inheritance child relation.- *- * The return value is the RelOptInfo that's added.- *- * PlanRowMarks still carry the top-parent's RTI, and the top-parent's- * allMarkTypes field still accumulates values from all descendents.- *- * "parentrte" and "parentRTindex" are immediate parent's RTE and- * RTI. "top_parentrc" is top parent's PlanRowMark.- */-static RelOptInfo *-add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte,- Index parentRTindex, RelOptInfo *parentrel,- PlanRowMark *top_parentrc, Relation childrel)-{- Query *parse = root->parse;- Oid childOID = RelationGetRelid(childrel);- RangeTblEntry *childrte;- Index childRTindex;- AppendRelInfo *appinfo;- RelOptInfo *childrelopt;-- /*- * Build an RTE for the child, and attach to query's rangetable list. We- * copy most fields of the parent's RTE, but replace relation OID and- * relkind, and set inh appropriately. Also, set requiredPerms to zero- * since all required permissions checks are done on the original RTE.- * Likewise, set the child's securityQuals to empty, because we only want- * to apply the parent's RLS conditions regardless of what RLS properties- * individual children may have. (This is an intentional choice to make- * inherited RLS work like regular permissions checks.) The parent- * securityQuals will be propagated to children along with other base- * restriction clauses, so we don't need to do it here.- */- childrte = copyObject(parentrte);- childrte->relid = childOID;- childrte->relkind = childrel->rd_rel->relkind;- /*- * A partitioned child will need to be expanded as an append parent- * itself, so set its inh to true.- */- childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE);- childrte->requiredPerms = 0;- childrte->securityQuals = NIL;- parse->rtable = lappend(parse->rtable, childrte);- childRTindex = list_length(parse->rtable);-- /* Create an AppendRelInfo and add it to planner's global list. */- appinfo = make_append_rel_info(parentrel, parentrte,- childrel->rd_att,- childOID,- childrel->rd_rel->reltype,- childRTindex);- root->append_rel_list = lappend(root->append_rel_list, appinfo);-- /*- * Translate the column permissions bitmaps to the child's attnums (we- * have to build the translated_vars list before we can do this). But- * if this is the parent table, leave copyObject's result alone.- *- * Note: we need to do this even though the executor won't run any- * permissions checks on the child RTE. The insertedCols/updatedCols- * bitmaps may be examined for trigger-firing purposes.- */- if (childrte->relid != parentrte->relid)- {- childrte->selectedCols = translate_col_privs(parentrte->selectedCols,- appinfo->translated_vars);- childrte->insertedCols = translate_col_privs(parentrte->insertedCols,- appinfo->translated_vars);- childrte->updatedCols = translate_col_privs(parentrte->updatedCols,- appinfo->translated_vars);- }-- /*- * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.- */- if (top_parentrc)- {- PlanRowMark *childrc = makeNode(PlanRowMark);-- childrc->rti = childRTindex;- childrc->prti = top_parentrc->rti;- childrc->rowmarkId = top_parentrc->rowmarkId;- /* Reselect rowmark type, because relkind might not match parent */- childrc->markType = select_rowmark_type(childrte,- top_parentrc->strength);- childrc->allMarkTypes = (1 << childrc->markType);- childrc->strength = top_parentrc->strength;- childrc->waitPolicy = top_parentrc->waitPolicy;-- /*- * We mark RowMarks for partitioned child tables as parent RowMarks so- * that the executor ignores them (except their existence means that- * the child tables be locked using appropriate mode).- */- childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);-- /* Include child's rowmark type in top parent's allMarkTypes */- top_parentrc->allMarkTypes |= childrc->allMarkTypes;-- root->rowMarks = lappend(root->rowMarks, childrc);- }-- /*- * Add the RelOptInfo. Even though we may not really scan this relation- * for reasons such as contradictory quals, we still need need to create- * one, because for every RTE in the query's range table, there must be an- * accompanying RelOptInfo.- */-- /* First, store the RTE and appinfos into planner arrays. */- 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;-- childrelopt = build_append_child_rel(root, parentrel, childRTindex);- Assert(childrelopt != NULL);-- return childrelopt;-}--/*- * make_inh_translation_list- * Build the list of translations from parent Vars to child Vars for- * an inheritance child.- *- * For paranoia's sake, we match type/collation as well as attribute name.- */-static void-make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc,- Oid from_rel, Oid to_rel,- Index newvarno, List **translated_vars)-{- List *vars = NIL;- int oldnatts = old_tupdesc->natts;- int newnatts = new_tupdesc->natts;- int old_attno;- int new_attno = 0;-- for (old_attno = 0; old_attno < oldnatts; old_attno++)- {- Form_pg_attribute att;- char *attname;- Oid atttypid;- int32 atttypmod;- Oid attcollation;-- att = TupleDescAttr(old_tupdesc, old_attno);- if (att->attisdropped)- {- /* Just put NULL into this list entry */- vars = lappend(vars, NULL);- continue;- }- attname = NameStr(att->attname);- atttypid = att->atttypid;- atttypmod = att->atttypmod;- attcollation = att->attcollation;-- /*- * When we are generating the "translation list" for the parent table- * of an inheritance set, no need to search for matches.- */- if (from_rel == to_rel)- {- vars = lappend(vars, makeVar(newvarno,- (AttrNumber) (old_attno + 1),- atttypid,- atttypmod,- attcollation,- 0));- continue;- }-- /*- * Otherwise we have to search for the matching column by name.- * There's no guarantee it'll have the same column position, because- * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.- * However, in simple cases, the relative order of columns is mostly- * the same in both relations, so try the column of newrelation that- * follows immediately after the one that we just found, and if that- * fails, let syscache handle it.- */- if (new_attno >= newnatts ||- (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped ||- strcmp(attname, NameStr(att->attname)) != 0)- {- HeapTuple newtup;-- newtup = SearchSysCacheAttName(to_rel, attname);- if (!newtup)- elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",- attname, get_rel_name(to_rel));- new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;- ReleaseSysCache(newtup);-- att = TupleDescAttr(new_tupdesc, new_attno);- }-- /* Found it, check type and collation match */- if (atttypid != att->atttypid || atttypmod != att->atttypmod)- elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",- attname, get_rel_name(to_rel));- if (attcollation != att->attcollation)- elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",- attname, get_rel_name(to_rel));-- vars = lappend(vars, makeVar(newvarno,- (AttrNumber) (new_attno + 1),- atttypid,- atttypmod,- attcollation,- 0));- new_attno++;- }-- *translated_vars = vars;-}--/*- * translate_col_privs- * Translate a bitmapset representing per-column privileges from the- * parent rel's attribute numbering to the child's.- *- * The only surprise here is that we don't translate a parent whole-row- * reference into a child whole-row reference. That would mean requiring- * permissions on all child columns, which is overly strict, since the- * query is really only going to reference the inherited columns. Instead- * we set the per-column bits for all inherited columns.- */-static Bitmapset *-translate_col_privs(const Bitmapset *parent_privs,- List *translated_vars)-{- Bitmapset *child_privs = NULL;- bool whole_row;- int attno;- ListCell *lc;-- /* System attributes have the same numbers in all tables */- for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)- {- if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,- parent_privs))- child_privs = bms_add_member(child_privs,- attno - FirstLowInvalidHeapAttributeNumber);- }-- /* Check if parent has whole-row reference */- whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,- parent_privs);-- /* And now translate the regular user attributes, using the vars list */- attno = InvalidAttrNumber;- foreach(lc, translated_vars)- {- Var *var = lfirst_node(Var, lc);-- attno++;- if (var == NULL) /* ignore dropped columns */- continue;- if (whole_row ||- bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,- parent_privs))- child_privs = bms_add_member(child_privs,- var->varattno - FirstLowInvalidHeapAttributeNumber);- }-- return child_privs;-}--/*- * build_append_child_rel- * Build a RelOptInfo for child relation of an append rel- *- * After creating the RelOptInfo for the given child RT index, it goes on to- * initialize some of its fields base on the parent RelOptInfo.- *- * If the quals in baserestrictinfo turns out to be self-contradictory, the- * RelOptInfo is marked dummy before returning.- */-static RelOptInfo *-build_append_child_rel(PlannerInfo *root,- RelOptInfo *parent,- Index childRTindex)-{- RelOptInfo *childrel;- RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];- AppendRelInfo *appinfo = root->append_rel_array[childRTindex];- List *childquals;- ListCell *lc;- bool have_const_false_cq;- Index cq_min_security;-- /* Build the RelOptInfo. */- childrel = build_simple_rel(root, childRTindex, parent);-- /*- * Propagate lateral_relids and lateral_referencers from appendrel- * parent rels to their child rels. We intentionally give each child rel- * the same minimum parameterization, even though it's quite possible that- * some don't reference all the lateral rels. This is because any append- * path for the parent will have to have the same parameterization for- * every child anyway, and there's no value in forcing extra- * reparameterize_path() calls. Similarly, a lateral reference to the- * parent prevents use of otherwise-movable join rels for each child.- */- childrel->direct_lateral_relids = parent->direct_lateral_relids;- childrel->lateral_relids = parent->lateral_relids;- childrel->lateral_referencers = parent->lateral_referencers;-- /*- * We have to copy the parent's quals to the child, with appropriate- * substitution of variables. However, only the baserestrictinfo- * quals are needed before we can check for constraint exclusion; so- * do that first and then check to see if we can disregard this child.- *- * The child rel's targetlist might contain non-Var expressions, which- * means that substitution into the quals could produce opportunities- * for const-simplification, and perhaps even pseudoconstant quals.- * Therefore, transform each RestrictInfo separately to see if it- * reduces to a constant or pseudoconstant. (We must process them- * separately to keep track of the security level of each qual.)- */- childquals = false;- cq_min_security = UINT_MAX;- have_const_false_cq = false;- foreach(lc, parent->baserestrictinfo)- {- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);- Node *childqual = (Node *) rinfo->clause;- ListCell *lc2;-- Assert(IsA(rinfo, RestrictInfo));- childqual = adjust_appendrel_attrs(root, childqual,- 1, &appinfo);- childqual = eval_const_expressions(root, childqual);- /* check for flat-out constant */- if (childqual && IsA(childqual, Const))- {- if (((Const *) childqual)->constisnull ||- !DatumGetBool(((Const *) childqual)->constvalue))- {- /* Restriction reduces to constant FALSE or NULL */- have_const_false_cq = true;- break;- }- /* Restriction reduces to constant TRUE, so drop it */- continue;- }- /* might have gotten an AND clause, if so flatten it */- foreach(lc2, make_ands_implicit((Expr *) childqual))- {- Node *onecq = (Node *) lfirst(lc2);- bool pseudoconstant;-- /* check for pseudoconstant (no Vars or volatile functions) */- pseudoconstant =- !contain_vars_of_level(onecq, 0) &&- !contain_volatile_functions(onecq);- if (pseudoconstant)- {- /* tell createplan.c to check for gating quals */- root->hasPseudoConstantQuals = true;- }- /* reconstitute RestrictInfo with appropriate properties */- childquals = lappend(childquals,- make_restrictinfo((Expr *) onecq,- rinfo->is_pushed_down,- rinfo->outerjoin_delayed,- pseudoconstant,- rinfo->security_level,- NULL, NULL, NULL));- cq_min_security = Min(cq_min_security, rinfo->security_level);- }- }-- /*- * In addition to the quals inherited from the parent, we might- * have securityQuals associated with this particular child node.- * (Currently this can only happen in appendrels originating from- * UNION ALL; inheritance child tables don't have their own- * securityQuals.) Pull any such securityQuals up into the- * baserestrictinfo for the child. This is similar to- * process_security_barrier_quals() for the parent rel, except- * that we can't make any general deductions from such quals,- * since they don't hold for the whole appendrel.- */- if (childRTE->securityQuals)- {- Index security_level = 0;-- foreach(lc, childRTE->securityQuals)- {- List *qualset = (List *) lfirst(lc);- ListCell *lc2;-- foreach(lc2, qualset)- {- Expr *qual = (Expr *) lfirst(lc2);-- /*- * not likely that we'd see constants here, so no- * check- */- childquals = lappend(childquals,- make_restrictinfo(qual,- true,- false,- false,- security_level,- NULL, NULL,- NULL));- cq_min_security = Min(cq_min_security, security_level);- }- security_level++;- }- Assert(security_level <= root->qual_security_level);- }-- /* Set child's version of baserestrictinfo. */- childrel->baserestrictinfo = childquals;- childrel->baserestrict_min_security = cq_min_security;-- if (have_const_false_cq)- {- /*- * Some restriction clause reduced to constant FALSE or NULL after- * substitution, so this child need not be scanned.- */- set_dummy_rel_pathlist(childrel);- }-- return childrel;-}--/*- * add_rowmark_junk_columns- * Add necessary junk columns for rowmarked inheritance parent rel.- *- * These values are needed for locking of rels selected FOR UPDATE/SHARE, and- * to do EvalPlanQual rechecking. See comments for PlanRowMark in- * plannodes.h.- */-static List *-add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc)-{- List *tlist = root->processed_tlist;- Var *var;- char resname[32];- TargetEntry *tle;-- if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))- {- /* Need to fetch TID */- var = makeVar(rc->rti,- SelfItemPointerAttributeNumber,- TIDOID,- -1,- InvalidOid,- 0);- snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);- tle = makeTargetEntry((Expr *) var,- list_length(tlist) + 1,- pstrdup(resname),- true);- tlist = lappend(tlist, tle);- }- if (rc->allMarkTypes & (1 << ROW_MARK_COPY))- {- /* Need the whole row as a junk var */- var = makeWholeRowVar(root->simple_rte_array[rc->rti],- rc->rti,- 0,- false);- snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);- tle = makeTargetEntry((Expr *) var,- list_length(tlist) + 1,- pstrdup(resname),- true);- tlist = lappend(tlist, tle);- }-- /* For inheritance cases, always fetch the tableoid too. */- var = makeVar(rc->rti,- TableOidAttributeNumber,- OIDOID,- -1,- InvalidOid,- 0);- snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);- tle = makeTargetEntry((Expr *) var,- list_length(tlist) + 1,- pstrdup(resname),- true);- tlist = lappend(tlist, tle);-- return tlist;-}--AppendRelInfo *-make_append_rel_info(RelOptInfo *parentrel, RangeTblEntry *parentrte,- TupleDesc childdesc, Oid childoid, Oid childreltype,- Index childRTindex)-{- AppendRelInfo *appinfo = makeNode(AppendRelInfo);-- appinfo->parent_relid = parentrel->relid;- appinfo->child_relid = childRTindex;- appinfo->parent_reltype = parentrel->reltype;- appinfo->child_reltype = childreltype;- make_inh_translation_list(parentrel->tupdesc, childdesc,- parentrte->relid, childoid,- childRTindex,- &appinfo->translated_vars);- appinfo->parent_reloid = parentrte->relid;-- return appinfo;-}--/*- * adjust_appendrel_attrs- * Copy the specified query or expression and translate Vars referring to a- * parent rel to refer to the corresponding child rel instead. We also- * update rtindexes appearing outside Vars, such as resultRelation and- * jointree relids.- *- * Note: this is only applied after conversion of sublinks to subplans,- * so we don't need to cope with recursion into sub-queries.- *- * Note: this is not hugely different from what pullup_replace_vars() does;- * maybe we should try to fold the two routines together.- */-Node *-adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,- AppendRelInfo **appinfos)-{- Node *result;- adjust_appendrel_attrs_context context;-- context.root = root;- context.nappinfos = nappinfos;- context.appinfos = appinfos;-- /* If there's nothing to adjust, don't call this function. */- Assert(nappinfos >= 1 && appinfos != NULL);-- /*- * Must be prepared to start with a Query or a bare expression tree.- */- if (node && IsA(node, Query))- {- Query *newnode;- int cnt;-- newnode = query_tree_mutator((Query *) node,- adjust_appendrel_attrs_mutator,- (void *) &context,- QTW_IGNORE_RC_SUBQUERIES);- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- if (newnode->resultRelation == appinfo->parent_relid)- {- newnode->resultRelation = appinfo->child_relid;- /* Fix tlist resnos too, if it's inherited UPDATE */- if (newnode->commandType == CMD_UPDATE)- newnode->targetList =- adjust_inherited_tlist(newnode->targetList,- appinfo);- break;- }- }-- result = (Node *) newnode;- }- else- result = adjust_appendrel_attrs_mutator(node, &context);-- return result;-}--static Node *-adjust_appendrel_attrs_mutator(Node *node,- adjust_appendrel_attrs_context *context)-{- AppendRelInfo **appinfos = context->appinfos;- int nappinfos = context->nappinfos;- int cnt;-- if (node == NULL)- return NULL;- if (IsA(node, Var))- {- Var *var = (Var *) copyObject(node);- AppendRelInfo *appinfo = NULL;-- for (cnt = 0; cnt < nappinfos; cnt++)- {- if (var->varno == appinfos[cnt]->parent_relid)- {- appinfo = appinfos[cnt];- break;- }- }-- if (var->varlevelsup == 0 && appinfo)- {- var->varno = appinfo->child_relid;- var->varnoold = appinfo->child_relid;- if (var->varattno > 0)- {- Node *newnode;-- if (var->varattno > list_length(appinfo->translated_vars))- elog(ERROR, "attribute %d of relation \"%s\" does not exist",- var->varattno, get_rel_name(appinfo->parent_reloid));- newnode = copyObject(list_nth(appinfo->translated_vars,- var->varattno - 1));- if (newnode == NULL)- elog(ERROR, "attribute %d of relation \"%s\" does not exist",- var->varattno, get_rel_name(appinfo->parent_reloid));- return newnode;- }- else if (var->varattno == 0)- {- /*- * Whole-row Var: if we are dealing with named rowtypes, we- * can use a whole-row Var for the child table plus a coercion- * step to convert the tuple layout to the parent's rowtype.- * Otherwise we have to generate a RowExpr.- */- if (OidIsValid(appinfo->child_reltype))- {- Assert(var->vartype == appinfo->parent_reltype);- if (appinfo->parent_reltype != appinfo->child_reltype)- {- ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);-- r->arg = (Expr *) var;- r->resulttype = appinfo->parent_reltype;- r->convertformat = COERCE_IMPLICIT_CAST;- r->location = -1;- /* Make sure the Var node has the right type ID, too */- var->vartype = appinfo->child_reltype;- return (Node *) r;- }- }- else- {- /*- * Build a RowExpr containing the translated variables.- *- * In practice var->vartype will always be RECORDOID here,- * so we need to come up with some suitable column names.- * We use the parent RTE's column names.- *- * Note: we can't get here for inheritance cases, so there- * is no need to worry that translated_vars might contain- * some dummy NULLs.- */- RowExpr *rowexpr;- List *fields;- RangeTblEntry *rte;-- rte = rt_fetch(appinfo->parent_relid,- context->root->parse->rtable);- fields = copyObject(appinfo->translated_vars);- rowexpr = makeNode(RowExpr);- rowexpr->args = fields;- rowexpr->row_typeid = var->vartype;- rowexpr->row_format = COERCE_IMPLICIT_CAST;- rowexpr->colnames = copyObject(rte->eref->colnames);- rowexpr->location = -1;-- return (Node *) rowexpr;- }- }- /* system attributes don't need any other translation */- }- return (Node *) var;- }- if (IsA(node, CurrentOfExpr))- {- CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);-- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- if (cexpr->cvarno == appinfo->parent_relid)- {- cexpr->cvarno = appinfo->child_relid;- break;- }- }- return (Node *) cexpr;- }- if (IsA(node, RangeTblRef))- {- RangeTblRef *rtr = (RangeTblRef *) copyObject(node);-- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- if (rtr->rtindex == appinfo->parent_relid)- {- rtr->rtindex = appinfo->child_relid;- break;- }- }- return (Node *) rtr;- }- if (IsA(node, JoinExpr))- {- /* Copy the JoinExpr node with correct mutation of subnodes */- JoinExpr *j;- AppendRelInfo *appinfo;-- j = (JoinExpr *) expression_tree_mutator(node,- adjust_appendrel_attrs_mutator,- (void *) context);- /* now fix JoinExpr's rtindex (probably never happens) */- for (cnt = 0; cnt < nappinfos; cnt++)- {- appinfo = appinfos[cnt];-- if (j->rtindex == appinfo->parent_relid)- {- j->rtindex = appinfo->child_relid;- break;- }- }- return (Node *) j;- }- if (IsA(node, PlaceHolderVar))- {- /* Copy the PlaceHolderVar node with correct mutation of subnodes */- PlaceHolderVar *phv;-- phv = (PlaceHolderVar *) expression_tree_mutator(node,- adjust_appendrel_attrs_mutator,- (void *) context);- /* now fix PlaceHolderVar's relid sets */- if (phv->phlevelsup == 0)- phv->phrels = adjust_child_relids(phv->phrels, context->nappinfos,- context->appinfos);- return (Node *) phv;- }-- /*- * This is needed, because inheritance_make_rel_from_joinlist needs to- * translate root->join_info_list executing make_rel_from_joinlist for a- * given child.- */- if (IsA(node, SpecialJoinInfo))- {- SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node;- SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo);-- memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo));- newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand,- context->nappinfos,- context->appinfos);- newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand,- context->nappinfos,- context->appinfos);- newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand,- context->nappinfos,- context->appinfos);- newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand,- context->nappinfos,- context->appinfos);- newinfo->semi_rhs_exprs =- (List *) expression_tree_mutator((Node *)- oldinfo->semi_rhs_exprs,- adjust_appendrel_attrs_mutator,- (void *) context);- return (Node *) newinfo;- }-- /* Shouldn't need to handle planner auxiliary nodes here */- Assert(!IsA(node, AppendRelInfo));- Assert(!IsA(node, PlaceHolderInfo));- Assert(!IsA(node, MinMaxAggInfo));-- /*- * We have to process RestrictInfo nodes specially. (Note: although- * set_append_rel_pathlist will hide RestrictInfos in the parent's- * baserestrictinfo list from us, it doesn't hide those in joininfo.)- */- if (IsA(node, RestrictInfo))- {- RestrictInfo *oldinfo = (RestrictInfo *) node;- RestrictInfo *newinfo = makeNode(RestrictInfo);-- /* Copy all flat-copiable fields */- memcpy(newinfo, oldinfo, sizeof(RestrictInfo));-- /* Recursively fix the clause itself */- newinfo->clause = (Expr *)- adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);-- /* and the modified version, if an OR clause */- newinfo->orclause = (Expr *)- adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);-- /* adjust relid sets too */- newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,- context->nappinfos,- context->appinfos);- newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,- context->nappinfos,- context->appinfos);- newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,- context->nappinfos,- context->appinfos);- newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids,- context->nappinfos,- context->appinfos);- newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,- context->nappinfos,- context->appinfos);- newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,- context->nappinfos,- context->appinfos);-- /*- * Reset cached derivative fields, since these might need to have- * different values when considering the child relation. Note we- * don't reset left_ec/right_ec: each child variable is implicitly- * equivalent to its parent, so still a member of the same EC if any.- */- newinfo->eval_cost.startup = -1;- newinfo->norm_selec = -1;- newinfo->outer_selec = -1;- newinfo->left_em = NULL;- newinfo->right_em = NULL;- newinfo->scansel_cache = NIL;- newinfo->left_bucketsize = -1;- newinfo->right_bucketsize = -1;- newinfo->left_mcvfreq = -1;- newinfo->right_mcvfreq = -1;-- return (Node *) newinfo;- }-- /*- * NOTE: we do not need to recurse into sublinks, because they should- * already have been converted to subplans before we see them.- */- Assert(!IsA(node, SubLink));- Assert(!IsA(node, Query));-- return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,- (void *) context);-}--/*- * Substitute child relids for parent relids in a Relid set. The array of- * appinfos specifies the substitutions to be performed.- */-static Relids-adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)-{- Bitmapset *result = NULL;- int cnt;-- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- /* Remove parent, add child */- if (bms_is_member(appinfo->parent_relid, relids))- {- /* Make a copy if we are changing the set. */- if (!result)- result = bms_copy(relids);-- result = bms_del_member(result, appinfo->parent_relid);- result = bms_add_member(result, appinfo->child_relid);- }- }-- /* If we made any changes, return the modified copy. */- if (result)- return result;-- /* Otherwise, return the original set without modification. */- return relids;-}--/*- * Replace any relid present in top_parent_relids with its child in- * child_relids. Members of child_relids can be multiple levels below top- * parent in the partition hierarchy.- */-Relids-adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,- Relids child_relids, Relids top_parent_relids)-{- AppendRelInfo **appinfos;- int nappinfos;- Relids parent_relids = NULL;- Relids result;- Relids tmp_result = NULL;- int cnt;-- /*- * If the given relids set doesn't contain any of the top parent relids,- * it will remain unchanged.- */- if (!bms_overlap(relids, top_parent_relids))- return relids;-- appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);-- /* Construct relids set for the immediate parent of the given child. */- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);- }-- /* Recurse if immediate parent is not the top parent. */- if (!bms_equal(parent_relids, top_parent_relids))- {- tmp_result = adjust_child_relids_multilevel(root, relids,- parent_relids,- top_parent_relids);- relids = tmp_result;- }-- result = adjust_child_relids(relids, nappinfos, appinfos);-- /* Free memory consumed by any intermediate result. */- if (tmp_result)- bms_free(tmp_result);- bms_free(parent_relids);- pfree(appinfos);-- return result;-}--/*- * Adjust the targetlist entries of an inherited UPDATE operation- *- * The expressions have already been fixed, but we have to make sure that- * the target resnos match the child table (they may not, in the case of- * a column that was added after-the-fact by ALTER TABLE). In some cases- * this can force us to re-order the tlist to preserve resno ordering.- * (We do all this work in special cases so that preptlist.c is fast for- * the typical case.)- *- * The given tlist has already been through expression_tree_mutator;- * therefore the TargetEntry nodes are fresh copies that it's okay to- * scribble on.- *- * Note that this is not needed for INSERT because INSERT isn't inheritable.- */-static List *-adjust_inherited_tlist(List *tlist, AppendRelInfo *context)-{- bool changed_it = false;- ListCell *tl;- List *new_tlist;- bool more;- int attrno;-- /* This should only happen for an inheritance case, not UNION ALL */- Assert(OidIsValid(context->parent_reloid));-- /* Scan tlist and update resnos to match attnums of child rel */- foreach(tl, tlist)- {- TargetEntry *tle = (TargetEntry *) lfirst(tl);- Var *childvar;-- if (tle->resjunk)- continue; /* ignore junk items */-- /* Look up the translation of this column: it must be a Var */- if (tle->resno <= 0 ||- tle->resno > list_length(context->translated_vars))- elog(ERROR, "attribute %d of relation \"%s\" does not exist",- tle->resno, get_rel_name(context->parent_reloid));- childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);- if (childvar == NULL || !IsA(childvar, Var))- elog(ERROR, "attribute %d of relation \"%s\" does not exist",- tle->resno, get_rel_name(context->parent_reloid));-- if (tle->resno != childvar->varattno)- {- tle->resno = childvar->varattno;- changed_it = true;- }- }-- /*- * If we changed anything, re-sort the tlist by resno, and make sure- * resjunk entries have resnos above the last real resno. The sort- * algorithm is a bit stupid, but for such a seldom-taken path, small is- * probably better than fast.- */- if (!changed_it)- return tlist;-- new_tlist = NIL;- more = true;- for (attrno = 1; more; attrno++)- {- more = false;- foreach(tl, tlist)- {- TargetEntry *tle = (TargetEntry *) lfirst(tl);-- if (tle->resjunk)- continue; /* ignore junk items */-- if (tle->resno == attrno)- new_tlist = lappend(new_tlist, tle);- else if (tle->resno > attrno)- more = true;- }- }-- foreach(tl, tlist)- {- TargetEntry *tle = (TargetEntry *) lfirst(tl);-- if (!tle->resjunk)- continue; /* here, ignore non-junk items */-- tle->resno = attrno;- new_tlist = lappend(new_tlist, tle);- attrno++;- }-- return new_tlist;-}--/*- * adjust_appendrel_attrs_multilevel- * Apply Var translations from a toplevel appendrel parent down to a child.- *- * In some cases we need to translate expressions referencing a parent relation- * to reference an appendrel child that's multiple levels removed from it.- */-Node *-adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,- Relids child_relids,- Relids top_parent_relids)-{- AppendRelInfo **appinfos;- Bitmapset *parent_relids = NULL;- int nappinfos;- int cnt;-- Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids));-- appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);-- /* Construct relids set for the immediate parent of given child. */- for (cnt = 0; cnt < nappinfos; cnt++)- {- AppendRelInfo *appinfo = appinfos[cnt];-- parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);- }-- /* Recurse if immediate parent is not the top parent. */- if (!bms_equal(parent_relids, top_parent_relids))- node = adjust_appendrel_attrs_multilevel(root, node, parent_relids,- top_parent_relids);-- /* Now translate for this child */- node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);-- pfree(appinfos);-- return node;-}--/*- * Construct the SpecialJoinInfo for a child-join by translating- * SpecialJoinInfo for the join between parents. left_relids and right_relids- * are the relids of left and right side of the join respectively.- */-SpecialJoinInfo *-build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,- Relids left_relids, Relids right_relids)-{- SpecialJoinInfo *sjinfo = makeNode(SpecialJoinInfo);- AppendRelInfo **left_appinfos;- int left_nappinfos;- AppendRelInfo **right_appinfos;- int right_nappinfos;-- memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));- left_appinfos = find_appinfos_by_relids(root, left_relids,- &left_nappinfos);- right_appinfos = find_appinfos_by_relids(root, right_relids,- &right_nappinfos);-- sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand,- left_nappinfos, left_appinfos);- sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand,- right_nappinfos,- right_appinfos);- sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand,- left_nappinfos, left_appinfos);- sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand,- right_nappinfos,- right_appinfos);- sjinfo->semi_rhs_exprs = (List *) adjust_appendrel_attrs(root,- (Node *) sjinfo->semi_rhs_exprs,- right_nappinfos,- right_appinfos);-- pfree(left_appinfos);- pfree(right_appinfos);-- return sjinfo;-}--/*- * find_appinfos_by_relids- * Find AppendRelInfo structures for all relations specified by relids.- *- * The AppendRelInfos are returned in an array, which can be pfree'd by the- * caller. *nappinfos is set to the number of entries in the array.- */-AppendRelInfo **-find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)-{- AppendRelInfo **appinfos;- int cnt = 0;- int i;-- *nappinfos = bms_num_members(relids);- appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos);-- i = -1;- while ((i = bms_next_member(relids, i)) >= 0)- {- AppendRelInfo *appinfo = root->append_rel_array[i];-- if (!appinfo)- elog(ERROR, "child rel %d not found in append_rel_array", i);-- appinfos[cnt++] = appinfo;- }- return appinfos;-}diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefileindex da9ccf32b4..d166030a10 100644--- a/src/backend/optimizer/util/Makefile+++ b/src/backend/optimizer/util/Makefile@@ -12,7 +12,8 @@ subdir = src/backend/optimizer/util
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = clauses.o joininfo.o orclauses.o pathnode.o placeholder.o \- plancat.o predtest.o relnode.o restrictinfo.o tlist.o var.o+OBJS = append.o appendinfo.o clauses.o joininfo.o orclauses.o pathnode.o \+ placeholder.o plancat.o predtest.o relnode.o restrictinfo.o tlist.o \+ var.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/optimizer/util/append.c b/src/backend/optimizer/util/append.cnew file mode 100644index 0000000000..bbc579817a--- /dev/null+++ b/src/backend/optimizer/util/append.c@@ -0,0 +1,719 @@+/*-------------------------------------------------------------------------+ *+ * append.c+ * Routines to process children of an appendrel parent+ *+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ *+ * IDENTIFICATION+ * src/backend/optimizer/path/append.c+ *+ *-------------------------------------------------------------------------+ */+#include "postgres.h"++#include <limits.h>++#include "access/sysattr.h"+#include "catalog/partition.h"+#include "catalog/pg_class.h"+#include "catalog/pg_inherits.h"+#include "catalog/pg_type.h"+#include "nodes/makefuncs.h"+#include "optimizer/append.h"+#include "optimizer/appendinfo.h"+#include "optimizer/clauses.h"+#include "optimizer/pathnode.h"+#include "optimizer/paths.h"+#include "optimizer/plancat.h"+#include "optimizer/planmain.h"+#include "optimizer/planner.h"+#include "optimizer/prep.h"+#include "optimizer/restrictinfo.h"+#include "optimizer/tlist.h"+#include "optimizer/var.h"+#include "partitioning/partprune.h"+#include "utils/rel.h"++static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,+ Index rti, RelOptInfo *rel);+static void expand_partitioned_rtentry(PlannerInfo *root,+ RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel);+static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root,+ RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel,+ PlanRowMark *top_parentrc, Relation childrel);+static RelOptInfo *build_append_child_rel(PlannerInfo *root,+ RelOptInfo *parent,+ Index childRTindex);+static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc);+++/*+ * expand_append_rel+ * This initializes RelOptInfos for an appendrel's child relations.+ *+ * 'rel' is the appendrel parent, whose range table entry ('rte') has been+ * marked to require adding children. An appendrel parent could either+ * be a subquery (if we flattened UNION ALL query) or a table that's known+ * to have inheritance children. The latter consists of both regular+ * inheritance parents and partitioned tables.+ *+ * For a subquery parent, there is not much to be done here because the+ * children's RTEs are already present in the query, so we just initialize+ * RelOptInfos for them. Also, the AppendRelInfos for child subqueries+ * have already been added.+ *+ * For tables, we need to add the children to the range table and initialize+ * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them. For+ * a partitioned parent, we only add the children remaining after pruning.+ * For regular inheritance parents, we find the children using+ * find_all_inheritors and add all of them.+ *+ * If it turns out that there are no children, then we set rte->inh to false+ * to let the caller know that only the parent table needs to be scanned. The+ * caller can accordingly switch to a non-Append path. For a partitioned+ * parent, that means an empty relation because parents themselves contain no+ * data.+ *+ * For the regular inheritance case, the parent also gets another RTE with+ * inh = false to represent it as an appendrel child. The original RTE is+ * considered to represent the whole inheritance set.+ */+void+expand_append_rel(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,+ Index rti)+{+ Assert(rte->inh);+ /* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */+ Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY);++ /*+ * UNION ALL children already got RTEs and AppendRelInfos, so just build+ * RelOptInfos and return.+ *+ * It might be a bit odd that this code is in this, because there is+ * nothing to expand really.+ */+ if (rte->rtekind == RTE_SUBQUERY)+ {+ ListCell *l;++ /*+ * We don't need to use expand_planner_arrays in this case, because+ * no new child RTEs are created. setup_simple_rel_arrays() and+ * setup_append_rel_array would've considered these child RTEs when+ * allocating space for various arrays.+ */+ foreach(l, root->append_rel_list)+ {+ AppendRelInfo *appinfo = lfirst(l);+ Index childRTindex = appinfo->child_relid;++ if (appinfo->parent_relid != rti)+ continue;++ Assert(childRTindex < root->simple_rel_array_size);+ Assert(root->simple_rte_array[childRTindex] != NULL);++ /*+ * We set the correct value of baserestricinfo and+ * baserestrict_min_security below.+ */+ root->simple_rel_array[childRTindex] =+ build_append_child_rel(root, rel, appinfo->child_relid);+ }+ }+ else+ {+ Assert(rte->rtekind == RTE_RELATION);+ Assert(has_subclass(rte->relid));++ /*+ * The rewriter should already have obtained an appropriate lock on+ * each relation named in the query. However, for each child relation+ * we add to the query, we must obtain an appropriate lock, because+ * this will be the first use of those relations in the+ * parse/rewrite/plan pipeline. Child rels should use the same+ * lockmode as their parent.+ */+ Assert(rte->rellockmode != NoLock);++ if (rte->relkind == RELKIND_PARTITIONED_TABLE)+ expand_partitioned_rtentry(root, rte, rti, rel);+ else+ expand_inherited_rtentry(root, rte, rti, rel);+ }+}++/*+ * expand_inherited_rtentry+ * Add entries for all the child tables to the query's rangetable, and+ * build AppendRelInfo nodes for all the child tables and add them to+ * root->append_rel_list.+ *+ * Note that the original RTE is considered to represent the whole+ * inheritance set. The first of the generated RTEs is an RTE for the same+ * table, but with inh = false, to represent the parent table in its role+ * as a simple member of the inheritance set.+ *+ * A childless table is never considered to be an inheritance set. For+ * regular inheritance, a parent RTE must always have at least two associated+ * AppendRelInfos: one corresponding to the parent table as a simple member of+ * inheritance set and one or more corresponding to the actual children.+ */+static void+expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti,+ RelOptInfo *rel)+{+ Oid parentOID;+ PlanRowMark *oldrc;+ LOCKMODE lockmode = rte->rellockmode;+ List *inhOIDs;+ ListCell *l;+ int num_children;+ int num_children_added = 0;++ Assert(rte->rtekind == RTE_RELATION);+ Assert(lockmode != NoLock);+ parentOID = rte->relid;++ /* Scan for all members of inheritance set, acquire needed locks */+ inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);++ /*+ * Check that there's at least one descendant, else treat as no-child+ * case. This could happen despite above has_subclass() check, if table+ * once had a child but no longer does.+ */+ num_children = list_length(inhOIDs);+ if (num_children < 2)+ {+ /* Clear flag before returning */+ rte->inh = false;+ return;+ }++ /*+ * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks+ * should've set isParent = true. We'll generate a new PlanRowMark for+ * each child.+ */+ oldrc = get_plan_rowmark(root->rowMarks, rti);+ Assert(oldrc == NULL || oldrc->isParent);++ /*+ * Must expand PlannerInfo arrays by num_children before we can add+ * children.+ */+ expand_planner_arrays(root, num_children);++ foreach(l, inhOIDs)+ {+ Oid childOID = lfirst_oid(l);+ Relation newrelation;+ RelOptInfo *childrel;++ /* Already locked above. */+ newrelation = heap_open(childOID, NoLock);++ /*+ * It is possible that the parent table has children that are temp+ * tables of other backends. We cannot safely access such tables+ * (because of buffering issues), and the best thing to do seems+ * to be to silently ignore them.+ */+ if (RELATION_IS_OTHER_TEMP(newrelation))+ {+ heap_close(newrelation, lockmode);+ continue;+ }++ childrel = add_inheritance_child_rel(root, rte, rti, rel, oldrc,+ newrelation);+ Assert(childrel != NULL);++ /* Close child relations, but keep locks */+ heap_close(newrelation, NoLock);+ num_children_added++;+ }++ /*+ * If all children, including the parent (as child rel), were+ * excluded, mark the parent rel as empty. If all the children were temp+ * tables, pretend it's a non-inheritance situation; we don't need Append+ * node in that case. The duplicate RTE we added for the parent table is+ * harmless, so we don't bother to get rid of it; ditto for the useless+ * PlanRowMark node.+ */+ if (num_children_added == 0)+ mark_dummy_rel(rel);+ else if (num_children_added == 1)+ rte->inh = false;++ /*+ * Add junk columns needed by the row mark if any and also add the+ * relevant expressions to the root parent's reltarget.+ */+ if (oldrc)+ {+ List *tlist = add_rowmark_junk_columns(root, oldrc);++ build_base_rel_tlists(root, tlist);+ }+}++/*+ * expand_partitioned_rtentry+ * Prunes unnecessary partitions of a partitioned table and adds+ * remaining ones to the Query and the PlannerInfo+ *+ * Partitions are added to the query in order in which they are found in+ * the parent's PartitionDesc.+ *+ * Note: even though only the unpruned partitions will be added to the+ * resulting plan, this still locks *all* partitions via find_all_inheritors+ * in order to avoid partitions being locked in a different order than other+ * places in the backend that may lock partitions.+ */+static void+expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel)+{+ LOCKMODE lockmode = parentrte->rellockmode;+ PlanRowMark *rootrc = NULL;+ int i;+ Bitmapset *partindexes;+ Index rootParentRTindex = parentrel->inh_root_parent > 0 ?+ parentrel->inh_root_parent :+ parentRTindex;++ /* If root partitioned table, lock *all* partitions in the tree. */+ if (parentRTindex == rootParentRTindex)+ (void) find_all_inheritors(parentrte->relid, lockmode, NULL);++ /*+ * Initialize partitioned_child_rels to contain this RT index.+ *+ * Note that during the set_append_rel_pathlist() phase, values of the+ * indexes of partitioned relations that appear down in the tree will be+ * bubbled up into root parent's list so that when we've created Paths for+ * all the children, the root table's list will contain all such indexes.+ */+ parentrel->partitioned_child_rels = list_make1_int(parentRTindex);++ /* Perform pruning. */+ partindexes = prune_append_rel_partitions(parentrel);++ /* Must expand PlannerInfo arrays before we can add children. */+ expand_planner_arrays(root, bms_num_members(partindexes));++ /*+ * For partitioned tables, we also store the partition RelOptInfo+ * pointers in the parent's RelOptInfo.+ */+ parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *+ parentrel->nparts);++ rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex);+ Assert(rootrc == NULL || rootrc->isParent);+ i = -1;+ while ((i = bms_next_member(partindexes, i)) >= 0)+ {+ Oid childOID = parentrel->part_oids[i];+ Relation newrelation;+ RelOptInfo *childrel;++ /* Already locked above. */+ newrelation = heap_open(childOID, NoLock);+ Assert(!RELATION_IS_OTHER_TEMP(newrelation));++ /*+ * A partitioned child table with 0 children is a dummy rel, so don't+ * bother creating planner objects for it.+ */+ if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&+ RelationGetPartitionDesc(newrelation)->nparts == 0)+ {+ heap_close(newrelation, NoLock);+ continue;+ }++ childrel = add_inheritance_child_rel(root, parentrte, parentRTindex,+ parentrel, rootrc, newrelation);+ Assert(childrel != NULL);+ parentrel->part_rels[i] = childrel;++ /* Close child relations, but keep locks */+ heap_close(newrelation, NoLock);+ }++ /*+ * Add junk columns needed by the row mark if any and also add the+ * relevant expressions to the root parent's reltarget.+ */+ if (rootrc)+ {+ List *tlist = add_rowmark_junk_columns(root, rootrc);++ build_base_rel_tlists(root, tlist);+ }+}++/*+ * add_inheritance_child_rel+ * Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally+ * a RelOptInfo for an inheritance child relation.+ *+ * The return value is the RelOptInfo that's added.+ *+ * PlanRowMarks still carry the top-parent's RTI, and the top-parent's+ * allMarkTypes field still accumulates values from all descendents.+ *+ * "parentrte" and "parentRTindex" are immediate parent's RTE and+ * RTI. "top_parentrc" is top parent's PlanRowMark.+ */+static RelOptInfo *+add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte,+ Index parentRTindex, RelOptInfo *parentrel,+ PlanRowMark *top_parentrc, Relation childrel)+{+ Query *parse = root->parse;+ Oid childOID = RelationGetRelid(childrel);+ RangeTblEntry *childrte;+ Index childRTindex;+ AppendRelInfo *appinfo;+ RelOptInfo *childrelopt;++ /*+ * Build an RTE for the child, and attach to query's rangetable list. We+ * copy most fields of the parent's RTE, but replace relation OID and+ * relkind, and set inh appropriately. Also, set requiredPerms to zero+ * since all required permissions checks are done on the original RTE.+ * Likewise, set the child's securityQuals to empty, because we only want+ * to apply the parent's RLS conditions regardless of what RLS properties+ * individual children may have. (This is an intentional choice to make+ * inherited RLS work like regular permissions checks.) The parent+ * securityQuals will be propagated to children along with other base+ * restriction clauses, so we don't need to do it here.+ */+ childrte = copyObject(parentrte);+ childrte->relid = childOID;+ childrte->relkind = childrel->rd_rel->relkind;+ /*+ * A partitioned child will need to be expanded as an append parent+ * itself, so set its inh to true.+ */+ childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE);+ childrte->requiredPerms = 0;+ childrte->securityQuals = NIL;+ parse->rtable = lappend(parse->rtable, childrte);+ childRTindex = list_length(parse->rtable);++ /* Create an AppendRelInfo and add it to planner's global list. */+ appinfo = make_append_rel_info(parentrel, parentrte,+ childrel->rd_att,+ childOID,+ childrel->rd_rel->reltype,+ childRTindex);+ root->append_rel_list = lappend(root->append_rel_list, appinfo);++ /*+ * Translate the column permissions bitmaps to the child's attnums (we+ * have to build the translated_vars list before we can do this). But+ * if this is the parent table, leave copyObject's result alone.+ *+ * Note: we need to do this even though the executor won't run any+ * permissions checks on the child RTE. The insertedCols/updatedCols+ * bitmaps may be examined for trigger-firing purposes.+ */+ if (childrte->relid != parentrte->relid)+ {+ childrte->selectedCols = translate_col_privs(parentrte->selectedCols,+ appinfo->translated_vars);+ childrte->insertedCols = translate_col_privs(parentrte->insertedCols,+ appinfo->translated_vars);+ childrte->updatedCols = translate_col_privs(parentrte->updatedCols,+ appinfo->translated_vars);+ }++ /*+ * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.+ */+ if (top_parentrc)+ {+ PlanRowMark *childrc = makeNode(PlanRowMark);++ childrc->rti = childRTindex;+ childrc->prti = top_parentrc->rti;+ childrc->rowmarkId = top_parentrc->rowmarkId;+ /* Reselect rowmark type, because relkind might not match parent */+ childrc->markType = select_rowmark_type(childrte,+ top_parentrc->strength);+ childrc->allMarkTypes = (1 << childrc->markType);+ childrc->strength = top_parentrc->strength;+ childrc->waitPolicy = top_parentrc->waitPolicy;++ /*+ * We mark RowMarks for partitioned child tables as parent RowMarks so+ * that the executor ignores them (except their existence means that+ * the child tables be locked using appropriate mode).+ */+ childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);++ /* Include child's rowmark type in top parent's allMarkTypes */+ top_parentrc->allMarkTypes |= childrc->allMarkTypes;++ root->rowMarks = lappend(root->rowMarks, childrc);+ }++ /*+ * Add the RelOptInfo. Even though we may not really scan this relation+ * for reasons such as contradictory quals, we still need need to create+ * one, because for every RTE in the query's range table, there must be an+ * accompanying RelOptInfo.+ */++ /* First, store the RTE and appinfos into planner arrays. */+ 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;++ childrelopt = build_append_child_rel(root, parentrel, childRTindex);+ Assert(childrelopt != NULL);++ return childrelopt;+}++/*+ * build_append_child_rel+ * Build a RelOptInfo for child relation of an append rel+ *+ * After creating the RelOptInfo for the given child RT index, it goes on to+ * initialize some of its fields base on the parent RelOptInfo.+ *+ * If the quals in baserestrictinfo turns out to be self-contradictory, the+ * RelOptInfo is marked dummy before returning.+ */+static RelOptInfo *+build_append_child_rel(PlannerInfo *root,+ RelOptInfo *parent,+ Index childRTindex)+{+ RelOptInfo *childrel;+ RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];+ AppendRelInfo *appinfo = root->append_rel_array[childRTindex];+ List *childquals;+ ListCell *lc;+ bool have_const_false_cq;+ Index cq_min_security;++ /* Build the RelOptInfo. */+ childrel = build_simple_rel(root, childRTindex, parent);++ /*+ * Propagate lateral_relids and lateral_referencers from appendrel+ * parent rels to their child rels. We intentionally give each child rel+ * the same minimum parameterization, even though it's quite possible that+ * some don't reference all the lateral rels. This is because any append+ * path for the parent will have to have the same parameterization for+ * every child anyway, and there's no value in forcing extra+ * reparameterize_path() calls. Similarly, a lateral reference to the+ * parent prevents use of otherwise-movable join rels for each child.+ */+ childrel->direct_lateral_relids = parent->direct_lateral_relids;+ childrel->lateral_relids = parent->lateral_relids;+ childrel->lateral_referencers = parent->lateral_referencers;++ /*+ * We have to copy the parent's quals to the child, with appropriate+ * substitution of variables. However, only the baserestrictinfo+ * quals are needed before we can check for constraint exclusion; so+ * do that first and then check to see if we can disregard this child.+ *+ * The child rel's targetlist might contain non-Var expressions, which+ * means that substitution into the quals could produce opportunities+ * for const-simplification, and perhaps even pseudoconstant quals.+ * Therefore, transform each RestrictInfo separately to see if it+ * reduces to a constant or pseudoconstant. (We must process them+ * separately to keep track of the security level of each qual.)+ */+ childquals = false;+ cq_min_security = UINT_MAX;+ have_const_false_cq = false;+ foreach(lc, parent->baserestrictinfo)+ {+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);+ Node *childqual = (Node *) rinfo->clause;+ ListCell *lc2;++ Assert(IsA(rinfo, RestrictInfo));+ childqual = adjust_appendrel_attrs(root, childqual,+ 1, &appinfo);+ childqual = eval_const_expressions(root, childqual);+ /* check for flat-out constant */+ if (childqual && IsA(childqual, Const))+ {+ if (((Const *) childqual)->constisnull ||+ !DatumGetBool(((Const *) childqual)->constvalue))+ {+ /* Restriction reduces to constant FALSE or NULL */+ have_const_false_cq = true;+ break;+ }+ /* Restriction reduces to constant TRUE, so drop it */+ continue;+ }+ /* might have gotten an AND clause, if so flatten it */+ foreach(lc2, make_ands_implicit((Expr *) childqual))+ {+ Node *onecq = (Node *) lfirst(lc2);+ bool pseudoconstant;++ /* check for pseudoconstant (no Vars or volatile functions) */+ pseudoconstant =+ !contain_vars_of_level(onecq, 0) &&+ !contain_volatile_functions(onecq);+ if (pseudoconstant)+ {+ /* tell createplan.c to check for gating quals */+ root->hasPseudoConstantQuals = true;+ }+ /* reconstitute RestrictInfo with appropriate properties */+ childquals = lappend(childquals,+ make_restrictinfo((Expr *) onecq,+ rinfo->is_pushed_down,+ rinfo->outerjoin_delayed,+ pseudoconstant,+ rinfo->security_level,+ NULL, NULL, NULL));+ cq_min_security = Min(cq_min_security, rinfo->security_level);+ }+ }++ /*+ * In addition to the quals inherited from the parent, we might+ * have securityQuals associated with this particular child node.+ * (Currently this can only happen in appendrels originating from+ * UNION ALL; inheritance child tables don't have their own+ * securityQuals.) Pull any such securityQuals up into the+ * baserestrictinfo for the child. This is similar to+ * process_security_barrier_quals() for the parent rel, except+ * that we can't make any general deductions from such quals,+ * since they don't hold for the whole appendrel.+ */+ if (childRTE->securityQuals)+ {+ Index security_level = 0;++ foreach(lc, childRTE->securityQuals)+ {+ List *qualset = (List *) lfirst(lc);+ ListCell *lc2;++ foreach(lc2, qualset)+ {+ Expr *qual = (Expr *) lfirst(lc2);++ /*+ * not likely that we'd see constants here, so no+ * check+ */+ childquals = lappend(childquals,+ make_restrictinfo(qual,+ true,+ false,+ false,+ security_level,+ NULL, NULL,+ NULL));+ cq_min_security = Min(cq_min_security, security_level);+ }+ security_level++;+ }+ Assert(security_level <= root->qual_security_level);+ }++ /* Set child's version of baserestrictinfo. */+ childrel->baserestrictinfo = childquals;+ childrel->baserestrict_min_security = cq_min_security;++ if (have_const_false_cq)+ {+ /*+ * Some restriction clause reduced to constant FALSE or NULL after+ * substitution, so this child need not be scanned.+ */+ set_dummy_rel_pathlist(childrel);+ }++ return childrel;+}++/*+ * add_rowmark_junk_columns+ * Add necessary junk columns for rowmarked inheritance parent rel.+ *+ * These values are needed for locking of rels selected FOR UPDATE/SHARE, and+ * to do EvalPlanQual rechecking. See comments for PlanRowMark in+ * plannodes.h.+ */+static List *+add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc)+{+ List *tlist = root->processed_tlist;+ Var *var;+ char resname[32];+ TargetEntry *tle;++ if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))+ {+ /* Need to fetch TID */+ var = makeVar(rc->rti,+ SelfItemPointerAttributeNumber,+ TIDOID,+ -1,+ InvalidOid,+ 0);+ snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);+ }+ if (rc->allMarkTypes & (1 << ROW_MARK_COPY))+ {+ /* Need the whole row as a junk var */+ var = makeWholeRowVar(root->simple_rte_array[rc->rti],+ rc->rti,+ 0,+ false);+ snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);+ }++ /* For inheritance cases, always fetch the tableoid too. */+ var = makeVar(rc->rti,+ TableOidAttributeNumber,+ OIDOID,+ -1,+ InvalidOid,+ 0);+ snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);+ tle = makeTargetEntry((Expr *) var,+ list_length(tlist) + 1,+ pstrdup(resname),+ true);+ tlist = lappend(tlist, tle);++ return tlist;+}diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.cnew file mode 100644index 0000000000..9502535e53--- /dev/null+++ b/src/backend/optimizer/util/appendinfo.c@@ -0,0 +1,851 @@+/*-------------------------------------------------------------------------+ *+ * appendinfo.c+ * Routines for mapping between append parent(s) and children+ *+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ *+ * IDENTIFICATION+ * src/backend/optimizer/path/appendinfo.c+ *+ *-------------------------------------------------------------------------+ */+#include "postgres.h"++#include "access/htup_details.h"+#include "access/sysattr.h"+#include "nodes/makefuncs.h"+#include "nodes/nodeFuncs.h"+#include "optimizer/appendinfo.h"+#include "parser/parsetree.h"+#include "utils/rel.h"+#include "utils/lsyscache.h"+#include "utils/syscache.h"++typedef struct+{+ PlannerInfo *root;+ int nappinfos;+ AppendRelInfo **appinfos;+} adjust_appendrel_attrs_context;++static void make_inh_translation_list(TupleDesc old_tupdesc,+ TupleDesc new_tupdesc,+ Oid from_rel, Oid to_rel,+ Index newvarno, List **translated_vars);+static Node *adjust_appendrel_attrs_mutator(Node *node,+ adjust_appendrel_attrs_context *context);+static Relids adjust_child_relids(Relids relids, int nappinfos,+ AppendRelInfo **appinfos);+static List *adjust_inherited_tlist(List *tlist,+ AppendRelInfo *context);++AppendRelInfo *+make_append_rel_info(RelOptInfo *parentrel, RangeTblEntry *parentrte,+ TupleDesc childdesc, Oid childoid, Oid childreltype,+ Index childRTindex)+{+ AppendRelInfo *appinfo = makeNode(AppendRelInfo);++ appinfo->parent_relid = parentrel->relid;+ appinfo->child_relid = childRTindex;+ appinfo->parent_reltype = parentrel->reltype;+ appinfo->child_reltype = childreltype;+ make_inh_translation_list(parentrel->tupdesc, childdesc,+ parentrte->relid, childoid,+ childRTindex,+ &appinfo->translated_vars);+ appinfo->parent_reloid = parentrte->relid;++ return appinfo;+}++/*+ * adjust_appendrel_attrs+ * Copy the specified query or expression and translate Vars referring to a+ * parent rel to refer to the corresponding child rel instead. We also+ * update rtindexes appearing outside Vars, such as resultRelation and+ * jointree relids.+ *+ * Note: this is only applied after conversion of sublinks to subplans,+ * so we don't need to cope with recursion into sub-queries.+ *+ * Note: this is not hugely different from what pullup_replace_vars() does;+ * maybe we should try to fold the two routines together.+ */+Node *+adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,+ AppendRelInfo **appinfos)+{+ Node *result;+ adjust_appendrel_attrs_context context;++ context.root = root;+ context.nappinfos = nappinfos;+ context.appinfos = appinfos;++ /* If there's nothing to adjust, don't call this function. */+ Assert(nappinfos >= 1 && appinfos != NULL);++ /*+ * Must be prepared to start with a Query or a bare expression tree.+ */+ if (node && IsA(node, Query))+ {+ Query *newnode;+ int cnt;++ newnode = query_tree_mutator((Query *) node,+ adjust_appendrel_attrs_mutator,+ (void *) &context,+ QTW_IGNORE_RC_SUBQUERIES);+ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ if (newnode->resultRelation == appinfo->parent_relid)+ {+ newnode->resultRelation = appinfo->child_relid;+ /* Fix tlist resnos too, if it's inherited UPDATE */+ if (newnode->commandType == CMD_UPDATE)+ newnode->targetList =+ adjust_inherited_tlist(newnode->targetList,+ appinfo);+ break;+ }+ }++ result = (Node *) newnode;+ }+ else+ result = adjust_appendrel_attrs_mutator(node, &context);++ return result;+}++static Node *+adjust_appendrel_attrs_mutator(Node *node,+ adjust_appendrel_attrs_context *context)+{+ AppendRelInfo **appinfos = context->appinfos;+ int nappinfos = context->nappinfos;+ int cnt;++ if (node == NULL)+ return NULL;+ if (IsA(node, Var))+ {+ Var *var = (Var *) copyObject(node);+ AppendRelInfo *appinfo = NULL;++ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ if (var->varno == appinfos[cnt]->parent_relid)+ {+ appinfo = appinfos[cnt];+ break;+ }+ }++ if (var->varlevelsup == 0 && appinfo)+ {+ var->varno = appinfo->child_relid;+ var->varnoold = appinfo->child_relid;+ if (var->varattno > 0)+ {+ Node *newnode;++ if (var->varattno > list_length(appinfo->translated_vars))+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",+ var->varattno, get_rel_name(appinfo->parent_reloid));+ newnode = copyObject(list_nth(appinfo->translated_vars,+ var->varattno - 1));+ if (newnode == NULL)+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",+ var->varattno, get_rel_name(appinfo->parent_reloid));+ return newnode;+ }+ else if (var->varattno == 0)+ {+ /*+ * Whole-row Var: if we are dealing with named rowtypes, we+ * can use a whole-row Var for the child table plus a coercion+ * step to convert the tuple layout to the parent's rowtype.+ * Otherwise we have to generate a RowExpr.+ */+ if (OidIsValid(appinfo->child_reltype))+ {+ Assert(var->vartype == appinfo->parent_reltype);+ if (appinfo->parent_reltype != appinfo->child_reltype)+ {+ ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);++ r->arg = (Expr *) var;+ r->resulttype = appinfo->parent_reltype;+ r->convertformat = COERCE_IMPLICIT_CAST;+ r->location = -1;+ /* Make sure the Var node has the right type ID, too */+ var->vartype = appinfo->child_reltype;+ return (Node *) r;+ }+ }+ else+ {+ /*+ * Build a RowExpr containing the translated variables.+ *+ * In practice var->vartype will always be RECORDOID here,+ * so we need to come up with some suitable column names.+ * We use the parent RTE's column names.+ *+ * Note: we can't get here for inheritance cases, so there+ * is no need to worry that translated_vars might contain+ * some dummy NULLs.+ */+ RowExpr *rowexpr;+ List *fields;+ RangeTblEntry *rte;++ rte = rt_fetch(appinfo->parent_relid,+ context->root->parse->rtable);+ fields = copyObject(appinfo->translated_vars);+ rowexpr = makeNode(RowExpr);+ rowexpr->args = fields;+ rowexpr->row_typeid = var->vartype;+ rowexpr->row_format = COERCE_IMPLICIT_CAST;+ rowexpr->colnames = copyObject(rte->eref->colnames);+ rowexpr->location = -1;++ return (Node *) rowexpr;+ }+ }+ /* system attributes don't need any other translation */+ }+ return (Node *) var;+ }+ if (IsA(node, CurrentOfExpr))+ {+ CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);++ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ if (cexpr->cvarno == appinfo->parent_relid)+ {+ cexpr->cvarno = appinfo->child_relid;+ break;+ }+ }+ return (Node *) cexpr;+ }+ if (IsA(node, RangeTblRef))+ {+ RangeTblRef *rtr = (RangeTblRef *) copyObject(node);++ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ if (rtr->rtindex == appinfo->parent_relid)+ {+ rtr->rtindex = appinfo->child_relid;+ break;+ }+ }+ return (Node *) rtr;+ }+ if (IsA(node, JoinExpr))+ {+ /* Copy the JoinExpr node with correct mutation of subnodes */+ JoinExpr *j;+ AppendRelInfo *appinfo;++ j = (JoinExpr *) expression_tree_mutator(node,+ adjust_appendrel_attrs_mutator,+ (void *) context);+ /* now fix JoinExpr's rtindex (probably never happens) */+ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ appinfo = appinfos[cnt];++ if (j->rtindex == appinfo->parent_relid)+ {+ j->rtindex = appinfo->child_relid;+ break;+ }+ }+ return (Node *) j;+ }+ if (IsA(node, PlaceHolderVar))+ {+ /* Copy the PlaceHolderVar node with correct mutation of subnodes */+ PlaceHolderVar *phv;++ phv = (PlaceHolderVar *) expression_tree_mutator(node,+ adjust_appendrel_attrs_mutator,+ (void *) context);+ /* now fix PlaceHolderVar's relid sets */+ if (phv->phlevelsup == 0)+ phv->phrels = adjust_child_relids(phv->phrels, context->nappinfos,+ context->appinfos);+ return (Node *) phv;+ }++ /*+ * This is needed, because inheritance_make_rel_from_joinlist needs to+ * translate root->join_info_list executing make_rel_from_joinlist for a+ * given child.+ */+ if (IsA(node, SpecialJoinInfo))+ {+ SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node;+ SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo);++ memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo));+ newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand,+ context->nappinfos,+ context->appinfos);+ newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand,+ context->nappinfos,+ context->appinfos);+ newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand,+ context->nappinfos,+ context->appinfos);+ newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand,+ context->nappinfos,+ context->appinfos);+ newinfo->semi_rhs_exprs =+ (List *) expression_tree_mutator((Node *)+ oldinfo->semi_rhs_exprs,+ adjust_appendrel_attrs_mutator,+ (void *) context);+ return (Node *) newinfo;+ }++ /* Shouldn't need to handle planner auxiliary nodes here */+ Assert(!IsA(node, AppendRelInfo));+ Assert(!IsA(node, PlaceHolderInfo));+ Assert(!IsA(node, MinMaxAggInfo));++ /*+ * We have to process RestrictInfo nodes specially. (Note: although+ * set_append_rel_pathlist will hide RestrictInfos in the parent's+ * baserestrictinfo list from us, it doesn't hide those in joininfo.)+ */+ if (IsA(node, RestrictInfo))+ {+ RestrictInfo *oldinfo = (RestrictInfo *) node;+ RestrictInfo *newinfo = makeNode(RestrictInfo);++ /* Copy all flat-copiable fields */+ memcpy(newinfo, oldinfo, sizeof(RestrictInfo));++ /* Recursively fix the clause itself */+ newinfo->clause = (Expr *)+ adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);++ /* and the modified version, if an OR clause */+ newinfo->orclause = (Expr *)+ adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);++ /* adjust relid sets too */+ newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,+ context->nappinfos,+ context->appinfos);+ newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,+ context->nappinfos,+ context->appinfos);++ /*+ * Reset cached derivative fields, since these might need to have+ * different values when considering the child relation. Note we+ * don't reset left_ec/right_ec: each child variable is implicitly+ * equivalent to its parent, so still a member of the same EC if any.+ */+ newinfo->eval_cost.startup = -1;+ newinfo->norm_selec = -1;+ newinfo->outer_selec = -1;+ newinfo->left_em = NULL;+ newinfo->right_em = NULL;+ newinfo->scansel_cache = NIL;+ newinfo->left_bucketsize = -1;+ newinfo->right_bucketsize = -1;+ newinfo->left_mcvfreq = -1;+ newinfo->right_mcvfreq = -1;++ return (Node *) newinfo;+ }++ /*+ * NOTE: we do not need to recurse into sublinks, because they should+ * already have been converted to subplans before we see them.+ */+ Assert(!IsA(node, SubLink));+ Assert(!IsA(node, Query));++ return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,+ (void *) context);+}++/*+ * Substitute child relids for parent relids in a Relid set. The array of+ * appinfos specifies the substitutions to be performed.+ */+static Relids+adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)+{+ Bitmapset *result = NULL;+ int cnt;++ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ /* Remove parent, add child */+ if (bms_is_member(appinfo->parent_relid, relids))+ {+ /* Make a copy if we are changing the set. */+ if (!result)+ result = bms_copy(relids);++ result = bms_del_member(result, appinfo->parent_relid);+ result = bms_add_member(result, appinfo->child_relid);+ }+ }++ /* If we made any changes, return the modified copy. */+ if (result)+ return result;++ /* Otherwise, return the original set without modification. */+ return relids;+}++/*+ * Replace any relid present in top_parent_relids with its child in+ * child_relids. Members of child_relids can be multiple levels below top+ * parent in the partition hierarchy.+ */+Relids+adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,+ Relids child_relids, Relids top_parent_relids)+{+ AppendRelInfo **appinfos;+ int nappinfos;+ Relids parent_relids = NULL;+ Relids result;+ Relids tmp_result = NULL;+ int cnt;++ /*+ * If the given relids set doesn't contain any of the top parent relids,+ * it will remain unchanged.+ */+ if (!bms_overlap(relids, top_parent_relids))+ return relids;++ appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);++ /* Construct relids set for the immediate parent of the given child. */+ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);+ }++ /* Recurse if immediate parent is not the top parent. */+ if (!bms_equal(parent_relids, top_parent_relids))+ {+ tmp_result = adjust_child_relids_multilevel(root, relids,+ parent_relids,+ top_parent_relids);+ relids = tmp_result;+ }++ result = adjust_child_relids(relids, nappinfos, appinfos);++ /* Free memory consumed by any intermediate result. */+ if (tmp_result)+ bms_free(tmp_result);+ bms_free(parent_relids);+ pfree(appinfos);++ return result;+}++/*+ * adjust_appendrel_attrs_multilevel+ * Apply Var translations from a toplevel appendrel parent down to a child.+ *+ * In some cases we need to translate expressions referencing a parent relation+ * to reference an appendrel child that's multiple levels removed from it.+ */+Node *+adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,+ Relids child_relids,+ Relids top_parent_relids)+{+ AppendRelInfo **appinfos;+ Bitmapset *parent_relids = NULL;+ int nappinfos;+ int cnt;++ Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids));++ appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);++ /* Construct relids set for the immediate parent of given child. */+ for (cnt = 0; cnt < nappinfos; cnt++)+ {+ AppendRelInfo *appinfo = appinfos[cnt];++ parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);+ }++ /* Recurse if immediate parent is not the top parent. */+ if (!bms_equal(parent_relids, top_parent_relids))+ node = adjust_appendrel_attrs_multilevel(root, node, parent_relids,+ top_parent_relids);++ /* Now translate for this child */+ node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);++ pfree(appinfos);++ return node;+}++/*+ * Construct the SpecialJoinInfo for a child-join by translating+ * SpecialJoinInfo for the join between parents. left_relids and right_relids+ * are the relids of left and right side of the join respectively.+ */+SpecialJoinInfo *+build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,+ Relids left_relids, Relids right_relids)+{+ SpecialJoinInfo *sjinfo = makeNode(SpecialJoinInfo);+ AppendRelInfo **left_appinfos;+ int left_nappinfos;+ AppendRelInfo **right_appinfos;+ int right_nappinfos;++ memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));+ left_appinfos = find_appinfos_by_relids(root, left_relids,+ &left_nappinfos);+ right_appinfos = find_appinfos_by_relids(root, right_relids,+ &right_nappinfos);++ sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand,+ left_nappinfos, left_appinfos);+ sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand,+ right_nappinfos,+ right_appinfos);+ sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand,+ left_nappinfos, left_appinfos);+ sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand,+ right_nappinfos,+ right_appinfos);+ sjinfo->semi_rhs_exprs = (List *) adjust_appendrel_attrs(root,+ (Node *) sjinfo->semi_rhs_exprs,+ right_nappinfos,+ right_appinfos);++ pfree(left_appinfos);+ pfree(right_appinfos);++ return sjinfo;+}++/*+ * find_appinfos_by_relids+ * Find AppendRelInfo structures for all relations specified by relids.+ *+ * The AppendRelInfos are returned in an array, which can be pfree'd by the+ * caller. *nappinfos is set to the number of entries in the array.+ */+AppendRelInfo **+find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)+{+ AppendRelInfo **appinfos;+ int cnt = 0;+ int i;++ *nappinfos = bms_num_members(relids);+ appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos);++ i = -1;+ while ((i = bms_next_member(relids, i)) >= 0)+ {+ AppendRelInfo *appinfo = root->append_rel_array[i];++ if (!appinfo)+ elog(ERROR, "child rel %d not found in append_rel_array", i);++ appinfos[cnt++] = appinfo;+ }+ return appinfos;+}++/*+ * make_inh_translation_list+ * Build the list of translations from parent Vars to child Vars for+ * an inheritance child.+ *+ * For paranoia's sake, we match type/collation as well as attribute name.+ */+static void+make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc,+ Oid from_rel, Oid to_rel,+ Index newvarno, List **translated_vars)+{+ List *vars = NIL;+ int oldnatts = old_tupdesc->natts;+ int newnatts = new_tupdesc->natts;+ int old_attno;+ int new_attno = 0;++ for (old_attno = 0; old_attno < oldnatts; old_attno++)+ {+ Form_pg_attribute att;+ char *attname;+ Oid atttypid;+ int32 atttypmod;+ Oid attcollation;++ att = TupleDescAttr(old_tupdesc, old_attno);+ if (att->attisdropped)+ {+ /* Just put NULL into this list entry */+ vars = lappend(vars, NULL);+ continue;+ }+ attname = NameStr(att->attname);+ atttypid = att->atttypid;+ atttypmod = att->atttypmod;+ attcollation = att->attcollation;++ /*+ * When we are generating the "translation list" for the parent table+ * of an inheritance set, no need to search for matches.+ */+ if (from_rel == to_rel)+ {+ vars = lappend(vars, makeVar(newvarno,+ (AttrNumber) (old_attno + 1),+ atttypid,+ atttypmod,+ attcollation,+ 0));+ continue;+ }++ /*+ * Otherwise we have to search for the matching column by name.+ * There's no guarantee it'll have the same column position, because+ * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.+ * However, in simple cases, the relative order of columns is mostly+ * the same in both relations, so try the column of newrelation that+ * follows immediately after the one that we just found, and if that+ * fails, let syscache handle it.+ */+ if (new_attno >= newnatts ||+ (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped ||+ strcmp(attname, NameStr(att->attname)) != 0)+ {+ HeapTuple newtup;++ newtup = SearchSysCacheAttName(to_rel, attname);+ if (!newtup)+ elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",+ attname, get_rel_name(to_rel));+ new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;+ ReleaseSysCache(newtup);++ att = TupleDescAttr(new_tupdesc, new_attno);+ }++ /* Found it, check type and collation match */+ if (atttypid != att->atttypid || atttypmod != att->atttypmod)+ elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",+ attname, get_rel_name(to_rel));+ if (attcollation != att->attcollation)+ elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",+ attname, get_rel_name(to_rel));++ vars = lappend(vars, makeVar(newvarno,+ (AttrNumber) (new_attno + 1),+ atttypid,+ atttypmod,+ attcollation,+ 0));+ new_attno++;+ }++ *translated_vars = vars;+}++/*+ * translate_col_privs+ * Translate a bitmapset representing per-column privileges from the+ * parent rel's attribute numbering to the child's.+ *+ * The only surprise here is that we don't translate a parent whole-row+ * reference into a child whole-row reference. That would mean requiring+ * permissions on all child columns, which is overly strict, since the+ * query is really only going to reference the inherited columns. Instead+ * we set the per-column bits for all inherited columns.+ */+Bitmapset *+translate_col_privs(const Bitmapset *parent_privs,+ List *translated_vars)+{+ Bitmapset *child_privs = NULL;+ bool whole_row;+ int attno;+ ListCell *lc;++ /* System attributes have the same numbers in all tables */+ for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)+ {+ if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,+ parent_privs))+ child_privs = bms_add_member(child_privs,+ attno - FirstLowInvalidHeapAttributeNumber);+ }++ /* Check if parent has whole-row reference */+ whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,+ parent_privs);++ /* And now translate the regular user attributes, using the vars list */+ attno = InvalidAttrNumber;+ foreach(lc, translated_vars)+ {+ Var *var = lfirst_node(Var, lc);++ attno++;+ if (var == NULL) /* ignore dropped columns */+ continue;+ if (whole_row ||+ bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,+ parent_privs))+ child_privs = bms_add_member(child_privs,+ var->varattno - FirstLowInvalidHeapAttributeNumber);+ }++ return child_privs;+}++/*+ * Adjust the targetlist entries of an inherited UPDATE operation+ *+ * The expressions have already been fixed, but we have to make sure that+ * the target resnos match the child table (they may not, in the case of+ * a column that was added after-the-fact by ALTER TABLE). In some cases+ * this can force us to re-order the tlist to preserve resno ordering.+ * (We do all this work in special cases so that preptlist.c is fast for+ * the typical case.)+ *+ * The given tlist has already been through expression_tree_mutator;+ * therefore the TargetEntry nodes are fresh copies that it's okay to+ * scribble on.+ *+ * Note that this is not needed for INSERT because INSERT isn't inheritable.+ */+static List *+adjust_inherited_tlist(List *tlist, AppendRelInfo *context)+{+ bool changed_it = false;+ ListCell *tl;+ List *new_tlist;+ bool more;+ int attrno;++ /* This should only happen for an inheritance case, not UNION ALL */+ Assert(OidIsValid(context->parent_reloid));++ /* Scan tlist and update resnos to match attnums of child rel */+ foreach(tl, tlist)+ {+ TargetEntry *tle = (TargetEntry *) lfirst(tl);+ Var *childvar;++ if (tle->resjunk)+ continue; /* ignore junk items */++ /* Look up the translation of this column: it must be a Var */+ if (tle->resno <= 0 ||+ tle->resno > list_length(context->translated_vars))+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",+ tle->resno, get_rel_name(context->parent_reloid));+ childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);+ if (childvar == NULL || !IsA(childvar, Var))+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",+ tle->resno, get_rel_name(context->parent_reloid));++ if (tle->resno != childvar->varattno)+ {+ tle->resno = childvar->varattno;+ changed_it = true;+ }+ }++ /*+ * If we changed anything, re-sort the tlist by resno, and make sure+ * resjunk entries have resnos above the last real resno. The sort+ * algorithm is a bit stupid, but for such a seldom-taken path, small is+ * probably better than fast.+ */+ if (!changed_it)+ return tlist;++ new_tlist = NIL;+ more = true;+ for (attrno = 1; more; attrno++)+ {+ more = false;+ foreach(tl, tlist)+ {+ TargetEntry *tle = (TargetEntry *) lfirst(tl);++ if (tle->resjunk)+ continue; /* ignore junk items */++ if (tle->resno == attrno)+ new_tlist = lappend(new_tlist, tle);+ else if (tle->resno > attrno)+ more = true;+ }+ }++ foreach(tl, tlist)+ {+ TargetEntry *tle = (TargetEntry *) lfirst(tl);++ if (!tle->resjunk)+ continue; /* here, ignore non-junk items */++ tle->resno = attrno;+ new_tlist = lappend(new_tlist, tle);+ attrno++;+ }++ return new_tlist;+}diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.cindex d50d86b252..8ce88876c4 100644--- a/src/backend/optimizer/util/pathnode.c+++ b/src/backend/optimizer/util/pathnode.c@@ -20,6 +20,7 @@
#include "foreign/fdwapi.h"
#include "nodes/extensible.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.cindex c23db9d78d..52a11f434f 100644--- a/src/backend/optimizer/util/relnode.c+++ b/src/backend/optimizer/util/relnode.c@@ -17,6 +17,7 @@
#include <limits.h>
#include "miscadmin.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.cindex bf17ae22ba..9120fcb0d3 100644--- a/src/backend/partitioning/partprune.c+++ b/src/backend/partitioning/partprune.c@@ -44,6 +44,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
diff --git a/src/include/optimizer/append.h b/src/include/optimizer/append.hnew file mode 100644index 0000000000..5b44530279--- /dev/null+++ b/src/include/optimizer/append.h@@ -0,0 +1,28 @@+/*-------------------------------------------------------------------------+ *+ * append.h+ * prototypes for append.c.+ *+ *+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/include/optimizer/append.h+ *+ *-------------------------------------------------------------------------+ */+#ifndef APPEND_H+#define APPEND_H++#include "nodes/relation.h"++/*+ * append.c+ * utilities for dealing with append relations+ */+extern void expand_append_rel(PlannerInfo *root,+ RelOptInfo *rel,+ RangeTblEntry *rte,+ Index rti);++#endif /* APPEND_H */diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.hnew file mode 100644index 0000000000..e205e78e6d--- /dev/null+++ b/src/include/optimizer/appendinfo.h@@ -0,0 +1,43 @@+/*-------------------------------------------------------------------------+ *+ * appendinfo.h+ * Routines for mapping expressions between append rel parent(s) and+ * children+ *+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/include/optimizer/appendinfo.h+ *+ *-------------------------------------------------------------------------+ */+#ifndef APPENDINFO_H+#define APPENDINFO_H++#include "nodes/plannodes.h"+#include "nodes/relation.h"+#include "utils/relcache.h"++extern AppendRelInfo *make_append_rel_info(RelOptInfo *parentrel,+ RangeTblEntry *parentrte,+ TupleDesc childdesc, Oid childoid, Oid childreltype,+ Index childRTindex);+extern Bitmapset *translate_col_privs(const Bitmapset *parent_privs,+ List *translated_vars);+extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,+ int nappinfos, AppendRelInfo **appinfos);++extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,+ Relids child_relids,+ Relids top_parent_relids);++extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root,+ Relids relids, int *nappinfos);++extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,+ SpecialJoinInfo *parent_sjinfo,+ Relids left_relids, Relids right_relids);+extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,+ Relids child_relids, Relids top_parent_relids);++#endif /* APPENDINFO_H */diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.hindex e8e21e2ed5..0e2cdbc539 100644--- a/src/include/optimizer/prep.h+++ b/src/include/optimizer/prep.h@@ -46,26 +46,5 @@ extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
* prototypes for prepunion.c
*/
extern RelOptInfo *plan_set_operations(PlannerInfo *root);
-extern void expand_append_rel(PlannerInfo *root,- RelOptInfo *rel,- RangeTblEntry *rte,- Index rti);-extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,- int nappinfos, AppendRelInfo **appinfos);-extern AppendRelInfo *make_append_rel_info(RelOptInfo *parentrel,- RangeTblEntry *parentrte,- TupleDesc childdesc, Oid childoid, Oid childreltype,- Index childRTindex);-extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,- Relids child_relids,- Relids top_parent_relids);-extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root,- Relids relids, int *nappinfos);--extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,- SpecialJoinInfo *parent_sjinfo,- Relids left_relids, Relids right_relids);-extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,- Relids child_relids, Relids top_parent_relids);
#endif /* PREP_H */
--
2.11.0
[text/plain] v5-0005-Teach-planner-to-only-process-unpruned-partitions.patch (7.2K, ../3be67028-a00a-502c-199a-da00eec8fb6e@lab.ntt.co.jp/6-v5-0005-Teach-planner-to-only-process-unpruned-partitions.patch)
download | inline diff:
From b869df16ad56eb7970085fe6cbf20cd0d5ab3be8 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Mon, 15 Oct 2018 10:59:24 +0900
Subject: [PATCH v5 5/6] Teach planner to only process unpruned partitions
This adds a bitmapset field live_parts to RelOptInfo and stores
the partition indexes of non-dummy partitions in it, meaning it
contains indexes of only those partitions that have a non-NULL
RelOptInfo present in its parent's RelOptInfo's part_rels array.
This speeds up processing partitioned table's partitions compared
to going through the whole part_rels array in a number of places.
---
src/backend/optimizer/path/joinrels.c | 3 +++
src/backend/optimizer/plan/planner.c | 18 +++++++++---------
src/backend/optimizer/util/append.c | 8 ++++++++
src/backend/optimizer/util/relnode.c | 3 +++
src/backend/partitioning/partprune.c | 18 +++++-------------
src/include/nodes/relation.h | 2 ++
6 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.cindex 6e321ec9e7..76531a05cf 100644--- a/src/backend/optimizer/path/joinrels.c+++ b/src/backend/optimizer/path/joinrels.c@@ -1424,6 +1424,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
populate_joinrel_with_paths(root, child_rel1, child_rel2,
child_joinrel, child_sjinfo,
child_restrictlist);
+ if (!IS_DUMMY_REL(child_joinrel))+ joinrel->live_parts = bms_add_member(joinrel->live_parts,+ cnt_parts);
}
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.cindex 216cadfba9..c60c491b61 100644--- a/src/backend/optimizer/plan/planner.c+++ b/src/backend/optimizer/plan/planner.c@@ -6766,7 +6766,9 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
List *live_children = NIL;
/* Adjust each partition. */
- for (partition_idx = 0; partition_idx < rel->nparts; partition_idx++)+ partition_idx = -1;+ while ((partition_idx = bms_next_member(rel->live_parts,+ partition_idx)) >= 0)
{
RelOptInfo *child_rel = rel->part_rels[partition_idx];
ListCell *lc;
@@ -6774,9 +6776,7 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
int nappinfos;
List *child_scanjoin_targets = NIL;
- /* Skip processing pruned partitions. */- if (child_rel == NULL)- continue;+ Assert(child_rel != NULL);
/* Translate scan/join targets for this child. */
appinfos = find_appinfos_by_relids(root, child_rel->relids,
@@ -6857,7 +6857,6 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
PartitionwiseAggregateType patype,
GroupPathExtraData *extra)
{
- int nparts = input_rel->nparts;
int cnt_parts;
List *grouped_live_children = NIL;
List *partially_grouped_live_children = NIL;
@@ -6869,7 +6868,9 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
partially_grouped_rel != NULL);
/* Add paths for partitionwise aggregation/grouping. */
- for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)+ cnt_parts = -1;+ while ((cnt_parts = bms_next_member(input_rel->live_parts,+ cnt_parts)) >= 0)
{
RelOptInfo *child_input_rel = input_rel->part_rels[cnt_parts];
PathTarget *child_target = copy_pathtarget(target);
@@ -6879,9 +6880,8 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
RelOptInfo *child_grouped_rel;
RelOptInfo *child_partially_grouped_rel;
- /* Skip processing pruned partitions. */- if (child_input_rel == NULL)- continue;+ /* A live partition must have a RelOptInfo. */+ Assert(child_input_rel != NULL);
/* Input child rel must have a path */
Assert(child_input_rel->pathlist != NIL);
diff --git a/src/backend/optimizer/util/append.c b/src/backend/optimizer/util/append.cindex bbc579817a..2ca8e8d0ff 100644--- a/src/backend/optimizer/util/append.c+++ b/src/backend/optimizer/util/append.c@@ -307,6 +307,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
/* Perform pruning. */
partindexes = prune_append_rel_partitions(parentrel);
+ parentrel->live_parts = partindexes;
/* Must expand PlannerInfo arrays before we can add children. */
expand_planner_arrays(root, bms_num_members(partindexes));
@@ -347,6 +348,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
Assert(childrel != NULL);
parentrel->part_rels[i] = childrel;
+ /*+ * If partition is excluded by constraints, remove it from+ * live_parts, too.+ */+ if (IS_DUMMY_REL(childrel))+ parentrel->live_parts = bms_del_member(parentrel->live_parts, i);+
/* Close child relations, but keep locks */
heap_close(newrelation, NoLock);
}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.cindex 52a11f434f..6c7eafe6db 100644--- a/src/backend/optimizer/util/relnode.c+++ b/src/backend/optimizer/util/relnode.c@@ -1748,6 +1748,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
joinrel->partexprs[cnt] = partexpr;
joinrel->nullable_partexprs[cnt] = nullable_partexpr;
}
++ /* Partitions will be added by try_partitionwise_join. */+ joinrel->live_parts = NULL;
}
/*
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.cindex 9120fcb0d3..2ed5c9e8dd 100644--- a/src/backend/partitioning/partprune.c+++ b/src/backend/partitioning/partprune.c@@ -441,29 +441,21 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
memset(subplan_map, -1, nparts * sizeof(int));
subpart_map = (int *) palloc(nparts * sizeof(int));
memset(subpart_map, -1, nparts * sizeof(int));
- present_parts = NULL;+ present_parts = bms_copy(subpart->live_parts);- for (i = 0; i < nparts; i++)+ i = -1;+ while ((i = bms_next_member(present_parts, i)) >= 0)
{
RelOptInfo *partrel = subpart->part_rels[i];
int subplanidx;
int subpartidx;
- /* Skip processing pruned partitions. */- if (partrel == NULL)- continue;-+ Assert(partrel != NULL);
subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
+ /* Record finding this subplan */
if (subplanidx >= 0)
- {- present_parts = bms_add_member(present_parts, i);-- /* Record finding this subplan */
subplansfound = bms_add_member(subplansfound, subplanidx);
- }- else if (subpartidx >= 0)- present_parts = bms_add_member(present_parts, i);
}
pinfo = makeNode(PartitionedRelPruneInfo);
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.hindex 6b614e4794..7e5e4e2c6a 100644--- a/src/include/nodes/relation.h+++ b/src/include/nodes/relation.h@@ -714,6 +714,8 @@ typedef struct RelOptInfo
Oid *part_oids; /* partition OIDs */
struct RelOptInfo **part_rels; /* Array of RelOptInfos of partitions,
* stored in the same order of bounds */
+ Bitmapset *live_parts; /* Set of live partitions; contains indexes+ * into part_rels array */
List **partexprs; /* Non-nullable partition key expressions. */
List **nullable_partexprs; /* Nullable partition key expressions. */
List *partitioned_child_rels; /* List of RT indexes. */
--
2.11.0
[text/plain] v5-0006-Do-not-lock-all-partitions-at-the-beginning.patch (1.2K, ../3be67028-a00a-502c-199a-da00eec8fb6e@lab.ntt.co.jp/7-v5-0006-Do-not-lock-all-partitions-at-the-beginning.patch)
download | inline diff:
From 289fff1a50deead4b2b743d62056b613a916c221 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 12 Oct 2018 19:12:53 +0900
Subject: [PATCH v5 6/6] Do not lock all partitions at the beginning
---
src/backend/optimizer/util/append.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/backend/optimizer/util/append.c b/src/backend/optimizer/util/append.cindex 2ca8e8d0ff..f1627aef33 100644--- a/src/backend/optimizer/util/append.c+++ b/src/backend/optimizer/util/append.c@@ -291,10 +291,6 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
parentrel->inh_root_parent :
parentRTindex;
- /* If root partitioned table, lock *all* partitions in the tree. */- if (parentRTindex == rootParentRTindex)- (void) find_all_inheritors(parentrte->relid, lockmode, NULL);-
/*
* Initialize partitioned_child_rels to contain this RT index.
*
@@ -328,8 +324,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
Relation newrelation;
RelOptInfo *childrel;
- /* Already locked above. */- newrelation = heap_open(childOID, NoLock);+ newrelation = heap_open(childOID, lockmode);
Assert(!RELATION_IS_OTHER_TEMP(newrelation));
/*
--
2.11.0
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: Langote_Amit_f8@lab.ntt.co.jp, alvherre@2ndquadrant.com, imai.yoshikazu@jp.fujitsu.com, david.rowley@2ndquadrant.com
Subject: Re: speeding up planning with partitions
In-Reply-To: <3be67028-a00a-502c-199a-da00eec8fb6e@lab.ntt.co.jp>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox