Skip to content

Commit 53bcf5e

Browse files
committed
Build "other rels" of appendrel baserels in a separate step.
Up to now, otherrel RelOptInfos were built at the same time as baserel RelOptInfos, thanks to recursion in build_simple_rel(). However, nothing in query_planner's preprocessing cares at all about otherrels, only baserels, so we don't really need to build them until just before we enter make_one_rel. This has two benefits: * create_lateral_join_info did a lot of extra work to propagate lateral-reference information from parents to the correct children. But if we delay creation of the children till after that, it's trivial (and much harder to break, too). * Since we have all the restriction quals correctly assigned to parent appendrels by this point, it'll be possible to do plan-time pruning and never make child RelOptInfos at all for partitions that can be pruned away. That's not done here, but will be later on. Amit Langote, reviewed at various times by Dilip Kumar, Jesper Pedersen, Yoshikazu Imai, and David Rowley Discussion: https://postgr.es/m/9d7c5112-cb99-6a47-d3be-cf1ee6862a1d@lab.ntt.co.jp
1 parent 8994cc6 commit 53bcf5e

File tree

6 files changed

+150
-105
lines changed

6 files changed

+150
-105
lines changed

src/backend/optimizer/path/allpaths.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
10291029

10301030
/*
10311031
* The child rel's RelOptInfo was already created during
1032-
* add_base_rels_to_query.
1032+
* add_other_rels_to_query.
10331033
*/
10341034
childrel = find_base_rel(root, childRTindex);
10351035
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);

src/backend/optimizer/plan/initsplan.c

Lines changed: 40 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,16 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
9090
* add_base_rels_to_query
9191
*
9292
* Scan the query's jointree and create baserel RelOptInfos for all
93-
* the base relations (ie, table, subquery, and function RTEs)
93+
* the base relations (e.g., table, subquery, and function RTEs)
9494
* appearing in the jointree.
9595
*
9696
* The initial invocation must pass root->parse->jointree as the value of
9797
* jtnode. Internally, the function recurses through the jointree.
9898
*
9999
* At the end of this process, there should be one baserel RelOptInfo for
100-
* every non-join RTE that is used in the query. Therefore, this routine
101-
* is the only place that should call build_simple_rel with reloptkind
102-
* RELOPT_BASEREL. (Note: build_simple_rel recurses internally to build
103-
* "other rel" RelOptInfos for the members of any appendrels we find here.)
100+
* every non-join RTE that is used in the query. Some of the baserels
101+
* may be appendrel parents, which will require additional "otherrel"
102+
* RelOptInfos for their member rels, but those are added later.
104103
*/
105104
void
106105
add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
@@ -133,6 +132,42 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
133132
(int) nodeTag(jtnode));
134133
}
135134

135+
/*
136+
* add_other_rels_to_query
137+
* create "otherrel" RelOptInfos for the children of appendrel baserels
138+
*
139+
* At the end of this process, there should be RelOptInfos for all relations
140+
* that will be scanned by the query.
141+
*/
142+
void
143+
add_other_rels_to_query(PlannerInfo *root)
144+
{
145+
int rti;
146+
147+
for (rti = 1; rti < root->simple_rel_array_size; rti++)
148+
{
149+
RelOptInfo *rel = root->simple_rel_array[rti];
150+
RangeTblEntry *rte = root->simple_rte_array[rti];
151+
152+
/* there may be empty slots corresponding to non-baserel RTEs */
153+
if (rel == NULL)
154+
continue;
155+
156+
/* Ignore any "otherrels" that were already added. */
157+
if (rel->reloptkind != RELOPT_BASEREL)
158+
continue;
159+
160+
/* If it's marked as inheritable, look for children. */
161+
if (rte->inh)
162+
{
163+
/* Only relation and subquery RTEs can have children. */
164+
Assert(rte->rtekind == RTE_RELATION ||
165+
rte->rtekind == RTE_SUBQUERY);
166+
add_appendrel_other_rels(root, rel, rti);
167+
}
168+
}
169+
}
170+
136171

137172
/*****************************************************************************
138173
*
@@ -419,7 +454,6 @@ void
419454
create_lateral_join_info(PlannerInfo *root)
420455
{
421456
bool found_laterals = false;
422-
Relids prev_parents PG_USED_FOR_ASSERTS_ONLY = NULL;
423457
Index rti;
424458
ListCell *lc;
425459

@@ -618,54 +652,6 @@ create_lateral_join_info(PlannerInfo *root)
618652
bms_add_member(brel2->lateral_referencers, rti);
619653
}
620654
}
621-
622-
/*
623-
* Lastly, propagate lateral_relids and lateral_referencers from appendrel
624-
* parent rels to their child rels. We intentionally give each child rel
625-
* the same minimum parameterization, even though it's quite possible that
626-
* some don't reference all the lateral rels. This is because any append
627-
* path for the parent will have to have the same parameterization for
628-
* every child anyway, and there's no value in forcing extra
629-
* reparameterize_path() calls. Similarly, a lateral reference to the
630-
* parent prevents use of otherwise-movable join rels for each child.
631-
*
632-
* It's possible for child rels to have their own children, in which case
633-
* the topmost parent's lateral info must be propagated all the way down.
634-
* This code handles that case correctly so long as append_rel_list has
635-
* entries for child relationships before grandchild relationships, which
636-
* is an okay assumption right now, but we'll need to be careful to
637-
* preserve it. The assertions below check for incorrect ordering.
638-
*/
639-
foreach(lc, root->append_rel_list)
640-
{
641-
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
642-
RelOptInfo *parentrel = root->simple_rel_array[appinfo->parent_relid];
643-
RelOptInfo *childrel = root->simple_rel_array[appinfo->child_relid];
644-
645-
/*
646-
* If we're processing a subquery of a query with inherited target rel
647-
* (cf. inheritance_planner), append_rel_list may contain entries for
648-
* tables that are not part of the current subquery and hence have no
649-
* RelOptInfo. Ignore them. We can ignore dead rels, too.
650-
*/
651-
if (parentrel == NULL || !IS_SIMPLE_REL(parentrel))
652-
continue;
653-
654-
/* Verify that children are processed before grandchildren */
655-
#ifdef USE_ASSERT_CHECKING
656-
prev_parents = bms_add_member(prev_parents, appinfo->parent_relid);
657-
Assert(!bms_is_member(appinfo->child_relid, prev_parents));
658-
#endif
659-
660-
/* OK, propagate info down */
661-
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
662-
Assert(childrel->direct_lateral_relids == NULL);
663-
childrel->direct_lateral_relids = parentrel->direct_lateral_relids;
664-
Assert(childrel->lateral_relids == NULL);
665-
childrel->lateral_relids = parentrel->lateral_relids;
666-
Assert(childrel->lateral_referencers == NULL);
667-
childrel->lateral_referencers = parentrel->lateral_referencers;
668-
}
669655
}
670656

671657

src/backend/optimizer/plan/planmain.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,13 @@ query_planner(PlannerInfo *root, List *tlist,
159159
setup_append_rel_array(root);
160160

161161
/*
162-
* Construct RelOptInfo nodes for all base relations in query, and
163-
* indirectly for all appendrel member relations ("other rels"). This
164-
* will give us a RelOptInfo for every "simple" (non-join) rel involved in
165-
* the query.
162+
* Construct RelOptInfo nodes for all base relations used in the query.
163+
* Appendrel member relations ("other rels") will be added later.
166164
*
167-
* Note: the reason we find the rels by searching the jointree and
168-
* appendrel list, rather than just scanning the rangetable, is that the
169-
* rangetable may contain RTEs for rels not actively part of the query,
170-
* for example views. We don't want to make RelOptInfos for them.
165+
* Note: the reason we find the baserels by searching the jointree, rather
166+
* than scanning the rangetable, is that the rangetable may contain RTEs
167+
* for rels not actively part of the query, for example views. We don't
168+
* want to make RelOptInfos for them.
171169
*/
172170
add_base_rels_to_query(root, (Node *) parse->jointree);
173171

@@ -259,6 +257,16 @@ query_planner(PlannerInfo *root, List *tlist,
259257
*/
260258
extract_restriction_or_clauses(root);
261259

260+
/*
261+
* Now expand appendrels by adding "otherrels" for their children. We
262+
* delay this to the end so that we have as much information as possible
263+
* available for each baserel, including all restriction clauses. That
264+
* let us prune away partitions that don't satisfy a restriction clause.
265+
* Also note that some information such as lateral_relids is propagated
266+
* from baserels to otherrels here, so we must have computed it already.
267+
*/
268+
add_other_rels_to_query(root);
269+
262270
/*
263271
* Ready to do the primary planning.
264272
*/

src/backend/optimizer/util/relnode.c

Lines changed: 90 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,10 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
166166
rel->cheapest_total_path = NULL;
167167
rel->cheapest_unique_path = NULL;
168168
rel->cheapest_parameterized_paths = NIL;
169-
rel->direct_lateral_relids = NULL;
170-
rel->lateral_relids = NULL;
171169
rel->relid = relid;
172170
rel->rtekind = rte->rtekind;
173171
/* min_attr, max_attr, attr_needed, attr_widths are set below */
174172
rel->lateral_vars = NIL;
175-
rel->lateral_referencers = NULL;
176173
rel->indexlist = NIL;
177174
rel->statlist = NIL;
178175
rel->pages = 0;
@@ -205,20 +202,44 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
205202
rel->partitioned_child_rels = NIL;
206203

207204
/*
208-
* Pass top parent's relids down the inheritance hierarchy. If the parent
209-
* has top_parent_relids set, it's a direct or an indirect child of the
210-
* top parent indicated by top_parent_relids. By extension this child is
211-
* also an indirect child of that parent.
205+
* Pass assorted information down the inheritance hierarchy.
212206
*/
213207
if (parent)
214208
{
209+
/*
210+
* Each direct or indirect child wants to know the relids of its
211+
* topmost parent.
212+
*/
215213
if (parent->top_parent_relids)
216214
rel->top_parent_relids = parent->top_parent_relids;
217215
else
218216
rel->top_parent_relids = bms_copy(parent->relids);
217+
218+
/*
219+
* Also propagate lateral-reference information from appendrel parent
220+
* rels to their child rels. We intentionally give each child rel the
221+
* same minimum parameterization, even though it's quite possible that
222+
* some don't reference all the lateral rels. This is because any
223+
* append path for the parent will have to have the same
224+
* parameterization for every child anyway, and there's no value in
225+
* forcing extra reparameterize_path() calls. Similarly, a lateral
226+
* reference to the parent prevents use of otherwise-movable join rels
227+
* for each child.
228+
*
229+
* It's possible for child rels to have their own children, in which
230+
* case the topmost parent's lateral info propagates all the way down.
231+
*/
232+
rel->direct_lateral_relids = parent->direct_lateral_relids;
233+
rel->lateral_relids = parent->lateral_relids;
234+
rel->lateral_referencers = parent->lateral_referencers;
219235
}
220236
else
237+
{
221238
rel->top_parent_relids = NULL;
239+
rel->direct_lateral_relids = NULL;
240+
rel->lateral_relids = NULL;
241+
rel->lateral_referencers = NULL;
242+
}
222243

223244
/* Check type of rtable entry */
224245
switch (rte->rtekind)
@@ -273,53 +294,80 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
273294
root->qual_security_level = Max(root->qual_security_level,
274295
list_length(rte->securityQuals));
275296

297+
return rel;
298+
}
299+
300+
/*
301+
* add_appendrel_other_rels
302+
* Add "other rel" RelOptInfos for the children of an appendrel baserel
303+
*
304+
* "rel" is a relation that (still) has the rte->inh flag set, meaning it
305+
* has appendrel children listed in root->append_rel_list. We need to build
306+
* a RelOptInfo for each child relation so that we can plan scans on them.
307+
* (The parent relation might be a partitioned table, a table with
308+
* traditional inheritance children, or a flattened UNION ALL subquery.)
309+
*/
310+
void
311+
add_appendrel_other_rels(PlannerInfo *root, RelOptInfo *rel, Index rti)
312+
{
313+
int cnt_parts = 0;
314+
ListCell *l;
315+
276316
/*
277-
* If this rel is an appendrel parent, recurse to build "other rel"
278-
* RelOptInfos for its children. They are "other rels" because they are
279-
* not in the main join tree, but we will need RelOptInfos to plan access
280-
* to them.
317+
* If rel is a partitioned table, then we also need to build a part_rels
318+
* array so that the child RelOptInfos can be conveniently accessed from
319+
* the parent.
281320
*/
282-
if (rte->inh)
321+
if (rel->part_scheme != NULL)
283322
{
284-
ListCell *l;
285-
int nparts = rel->nparts;
286-
int cnt_parts = 0;
287-
288-
if (nparts > 0)
289-
rel->part_rels = (RelOptInfo **)
290-
palloc(sizeof(RelOptInfo *) * nparts);
323+
Assert(rel->nparts > 0);
324+
rel->part_rels = (RelOptInfo **)
325+
palloc0(sizeof(RelOptInfo *) * rel->nparts);
326+
}
291327

292-
foreach(l, root->append_rel_list)
293-
{
294-
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
295-
RelOptInfo *childrel;
328+
foreach(l, root->append_rel_list)
329+
{
330+
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
331+
Index childRTindex = appinfo->child_relid;
332+
RangeTblEntry *childrte;
333+
RelOptInfo *childrel;
296334

297-
/* append_rel_list contains all append rels; ignore others */
298-
if (appinfo->parent_relid != relid)
299-
continue;
335+
/* append_rel_list contains all append rels; ignore others */
336+
if (appinfo->parent_relid != rti)
337+
continue;
300338

301-
childrel = build_simple_rel(root, appinfo->child_relid,
302-
rel);
339+
/* find the child RTE, which should already exist */
340+
Assert(childRTindex < root->simple_rel_array_size);
341+
childrte = root->simple_rte_array[childRTindex];
342+
Assert(childrte != NULL);
303343

304-
/* Nothing more to do for an unpartitioned table. */
305-
if (!rel->part_scheme)
306-
continue;
344+
/* build child RelOptInfo, and add to main query data structures */
345+
childrel = build_simple_rel(root, childRTindex, rel);
307346

308-
/*
309-
* The order of partition OIDs in append_rel_list is the same as
310-
* the order in the PartitionDesc, so the order of part_rels will
311-
* also match the PartitionDesc. See expand_partitioned_rtentry.
312-
*/
313-
Assert(cnt_parts < nparts);
314-
rel->part_rels[cnt_parts] = childrel;
315-
cnt_parts++;
347+
/*
348+
* If rel is a partitioned table, fill in the part_rels array. The
349+
* order in which child tables appear in append_rel_list is the same
350+
* as the order in which they appear in the parent's PartitionDesc, so
351+
* assigning partitions like this works.
352+
*/
353+
if (rel->part_scheme != NULL)
354+
{
355+
Assert(cnt_parts < rel->nparts);
356+
rel->part_rels[cnt_parts++] = childrel;
316357
}
317358

318-
/* We should have seen all the child partitions. */
319-
Assert(cnt_parts == nparts);
359+
/* Child may itself be an inherited relation. */
360+
if (childrte->inh)
361+
{
362+
/* Only relation and subquery RTEs can have children. */
363+
Assert(childrte->rtekind == RTE_RELATION ||
364+
childrte->rtekind == RTE_SUBQUERY);
365+
add_appendrel_other_rels(root, childrel, childRTindex);
366+
}
320367
}
321368

322-
return rel;
369+
/* We should have filled all of the part_rels array if it's partitioned */
370+
Assert(cnt_parts == rel->nparts);
323371
}
324372

325373
/*

src/include/optimizer/pathnode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ extern void setup_simple_rel_arrays(PlannerInfo *root);
279279
extern void setup_append_rel_array(PlannerInfo *root);
280280
extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
281281
RelOptInfo *parent);
282+
extern void add_appendrel_other_rels(PlannerInfo *root, RelOptInfo *rel,
283+
Index rti);
282284
extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
283285
extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
284286
extern RelOptInfo *build_join_rel(PlannerInfo *root,

src/include/optimizer/planmain.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ extern int from_collapse_limit;
6565
extern int join_collapse_limit;
6666

6767
extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode);
68+
extern void add_other_rels_to_query(PlannerInfo *root);
6869
extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist);
6970
extern void add_vars_to_targetlist(PlannerInfo *root, List *vars,
7071
Relids where_needed, bool create_new_ph);

0 commit comments

Comments
 (0)