Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * prepunion.c
4 : : * Routines to plan set-operation queries. The filename is a leftover
5 : : * from a time when only UNIONs were implemented.
6 : : *
7 : : * There are two code paths in the planner for set-operation queries.
8 : : * If a subquery consists entirely of simple UNION ALL operations, it
9 : : * is converted into an "append relation". Otherwise, it is handled
10 : : * by the general code in this module (plan_set_operations and its
11 : : * subroutines). There is some support code here for the append-relation
12 : : * case, but most of the heavy lifting for that is done elsewhere,
13 : : * notably in prepjointree.c and allpaths.c.
14 : : *
15 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
16 : : * Portions Copyright (c) 1994, Regents of the University of California
17 : : *
18 : : *
19 : : * IDENTIFICATION
20 : : * src/backend/optimizer/prep/prepunion.c
21 : : *
22 : : *-------------------------------------------------------------------------
23 : : */
24 : : #include "postgres.h"
25 : :
26 : : #include <math.h>
27 : :
28 : : #include "access/htup_details.h"
29 : : #include "catalog/pg_type.h"
30 : : #include "miscadmin.h"
31 : : #include "nodes/makefuncs.h"
32 : : #include "nodes/nodeFuncs.h"
33 : : #include "optimizer/cost.h"
34 : : #include "optimizer/pathnode.h"
35 : : #include "optimizer/paths.h"
36 : : #include "optimizer/planner.h"
37 : : #include "optimizer/prep.h"
38 : : #include "optimizer/tlist.h"
39 : : #include "parser/parse_coerce.h"
40 : : #include "port/pg_bitutils.h"
41 : : #include "utils/selfuncs.h"
42 : :
43 : :
44 : : static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
45 : : SetOperationStmt *parentOp,
46 : : List *colTypes, List *colCollations,
47 : : List *refnames_tlist,
48 : : List **pTargetList,
49 : : bool *istrivial_tlist);
50 : : static RelOptInfo *generate_recursion_path(SetOperationStmt *setOp,
51 : : PlannerInfo *root,
52 : : List *refnames_tlist,
53 : : List **pTargetList);
54 : : static void build_setop_child_paths(PlannerInfo *root, RelOptInfo *rel,
55 : : bool trivial_tlist, List *child_tlist,
56 : : List *interesting_pathkeys,
57 : : double *pNumGroups);
58 : : static RelOptInfo *generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
59 : : List *refnames_tlist,
60 : : List **pTargetList);
61 : : static RelOptInfo *generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
62 : : List *refnames_tlist,
63 : : List **pTargetList);
64 : : static List *plan_union_children(PlannerInfo *root,
65 : : SetOperationStmt *top_union,
66 : : List *refnames_tlist,
67 : : List **tlist_list,
68 : : List **istrivial_tlist);
69 : : static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel);
70 : : static List *generate_setop_tlist(List *colTypes, List *colCollations,
71 : : Index varno,
72 : : bool hack_constants,
73 : : List *input_tlist,
74 : : List *refnames_tlist,
75 : : bool *trivial_tlist);
76 : : static List *generate_append_tlist(List *colTypes, List *colCollations,
77 : : List *input_tlists,
78 : : List *refnames_tlist);
79 : : static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
80 : : static PathTarget *create_setop_pathtarget(PlannerInfo *root, List *tlist,
81 : : List *child_pathlist);
82 : :
83 : :
84 : : /*
85 : : * plan_set_operations
86 : : *
87 : : * Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT)
88 : : *
89 : : * This routine only deals with the setOperations tree of the given query.
90 : : * Any top-level ORDER BY requested in root->parse->sortClause will be handled
91 : : * when we return to grouping_planner; likewise for LIMIT.
92 : : *
93 : : * What we return is an "upperrel" RelOptInfo containing at least one Path
94 : : * that implements the set-operation tree. In addition, root->processed_tlist
95 : : * receives a targetlist representing the output of the topmost setop node.
96 : : */
97 : : RelOptInfo *
98 : 5005 : plan_set_operations(PlannerInfo *root)
99 : : {
100 : 5005 : Query *parse = root->parse;
101 : 5005 : SetOperationStmt *topop = castNode(SetOperationStmt, parse->setOperations);
102 : : Node *node;
103 : : RangeTblEntry *leftmostRTE;
104 : : Query *leftmostQuery;
105 : : RelOptInfo *setop_rel;
106 : : List *top_tlist;
107 : :
108 : : Assert(topop);
109 : :
110 : : /* check for unsupported stuff */
111 : : Assert(parse->jointree->fromlist == NIL);
112 : : Assert(parse->jointree->quals == NULL);
113 : : Assert(parse->groupClause == NIL);
114 : : Assert(parse->havingQual == NULL);
115 : : Assert(parse->windowClause == NIL);
116 : : Assert(parse->distinctClause == NIL);
117 : :
118 : : /*
119 : : * In the outer query level, equivalence classes are limited to classes
120 : : * which define that the top-level target entry is equivalent to the
121 : : * corresponding child target entry. There won't be any equivalence class
122 : : * merging. Mark that merging is complete to allow us to make pathkeys.
123 : : */
124 : : Assert(root->eq_classes == NIL);
125 : 5005 : root->ec_merging_done = true;
126 : :
127 : : /*
128 : : * We'll need to build RelOptInfos for each of the leaf subqueries, which
129 : : * are RTE_SUBQUERY rangetable entries in this Query. Prepare the index
130 : : * arrays for those, and for AppendRelInfos in case they're needed.
131 : : */
132 : 5005 : setup_simple_rel_arrays(root);
133 : :
134 : : /*
135 : : * Find the leftmost component Query. We need to use its column names for
136 : : * all generated tlists (else SELECT INTO won't work right).
137 : : */
138 : 5005 : node = topop->larg;
139 [ + - + + ]: 8043 : while (node && IsA(node, SetOperationStmt))
140 : 3038 : node = ((SetOperationStmt *) node)->larg;
141 : : Assert(node && IsA(node, RangeTblRef));
142 : 5005 : leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex];
143 : 5005 : leftmostQuery = leftmostRTE->subquery;
144 : : Assert(leftmostQuery != NULL);
145 : :
146 : : /*
147 : : * If the topmost node is a recursive union, it needs special processing.
148 : : */
149 [ + + ]: 5005 : if (root->hasRecursion)
150 : : {
151 : 697 : setop_rel = generate_recursion_path(topop, root,
152 : : leftmostQuery->targetList,
153 : : &top_tlist);
154 : : }
155 : : else
156 : : {
157 : : bool trivial_tlist;
158 : :
159 : : /*
160 : : * Recurse on setOperations tree to generate paths for set ops. The
161 : : * final output paths should have just the column types shown as the
162 : : * output from the top-level node.
163 : : */
164 : 4308 : setop_rel = recurse_set_operations((Node *) topop, root,
165 : : NULL, /* no parent */
166 : : topop->colTypes, topop->colCollations,
167 : : leftmostQuery->targetList,
168 : : &top_tlist,
169 : : &trivial_tlist);
170 : : }
171 : :
172 : : /* Must return the built tlist into root->processed_tlist. */
173 : 5001 : root->processed_tlist = top_tlist;
174 : :
175 : 5001 : return setop_rel;
176 : : }
177 : :
178 : : /*
179 : : * recurse_set_operations
180 : : * Recursively handle one step in a tree of set operations
181 : : *
182 : : * setOp: current step (could be a SetOperationStmt or a leaf RangeTblRef)
183 : : * parentOp: parent step, or NULL if none (but see below)
184 : : * colTypes: OID list of set-op's result column datatypes
185 : : * colCollations: OID list of set-op's result column collations
186 : : * refnames_tlist: targetlist to take column names from
187 : : *
188 : : * parentOp should be passed as NULL unless that step is interested in
189 : : * getting sorted output from this step. ("Sorted" means "sorted according
190 : : * to the default btree opclasses of the result column datatypes".)
191 : : *
192 : : * Returns a RelOptInfo for the subtree, as well as these output parameters:
193 : : * *pTargetList: receives the fully-fledged tlist for the subtree's top plan
194 : : * *istrivial_tlist: true if, and only if, datatypes between parent and child
195 : : * match.
196 : : *
197 : : * If setOp is a leaf node, this function plans the sub-query but does
198 : : * not populate the pathlist of the returned RelOptInfo. The caller will
199 : : * generate SubqueryScan paths using useful path(s) of the subquery (see
200 : : * build_setop_child_paths). But this function does build the paths for
201 : : * set-operation nodes.
202 : : *
203 : : * The pTargetList output parameter is mostly redundant with the pathtarget
204 : : * of the returned RelOptInfo, but for the moment we need it because much of
205 : : * the logic in this file depends on flag columns being marked resjunk.
206 : : * XXX Now that there are no flag columns and hence no resjunk columns, we
207 : : * could probably refactor this file to deal only in pathtargets.
208 : : *
209 : : * We don't have to care about typmods here: the only allowed difference
210 : : * between set-op input and output typmods is input is a specific typmod
211 : : * and output is -1, and that does not require a coercion.
212 : : */
213 : : static RelOptInfo *
214 : 17586 : recurse_set_operations(Node *setOp, PlannerInfo *root,
215 : : SetOperationStmt *parentOp,
216 : : List *colTypes, List *colCollations,
217 : : List *refnames_tlist,
218 : : List **pTargetList,
219 : : bool *istrivial_tlist)
220 : : {
221 : : RelOptInfo *rel;
222 : :
223 : 17586 : *istrivial_tlist = true; /* for now */
224 : :
225 : : /* Guard against stack overflow due to overly complex setop nests */
226 : 17586 : check_stack_depth();
227 : :
228 [ + + ]: 17586 : if (IsA(setOp, RangeTblRef))
229 : : {
230 : 13108 : RangeTblRef *rtr = (RangeTblRef *) setOp;
231 : 13108 : RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
232 : 13108 : Query *subquery = rte->subquery;
233 : : PlannerInfo *subroot;
234 : : List *tlist;
235 : : bool trivial_tlist;
236 : : char *plan_name;
237 : :
238 : : Assert(subquery != NULL);
239 : :
240 : : /* Build a RelOptInfo for this leaf subquery. */
241 : 13108 : rel = build_simple_rel(root, rtr->rtindex, NULL);
242 : :
243 : : /* plan_params should not be in use in current query level */
244 : : Assert(root->plan_params == NIL);
245 : :
246 : : /*
247 : : * Generate a subroot and Paths for the subquery. If we have a
248 : : * parentOp, pass that down to encourage subquery_planner to consider
249 : : * suitably-sorted Paths.
250 : : */
251 : 13108 : plan_name = choose_plan_name(root->glob, "setop", true);
252 : 13108 : subroot = rel->subroot = subquery_planner(root->glob, subquery,
253 : : plan_name, root, NULL,
254 : : false, root->tuple_fraction,
255 : : parentOp);
256 : :
257 : : /*
258 : : * It should not be possible for the primitive query to contain any
259 : : * cross-references to other primitive queries in the setop tree.
260 : : */
261 [ - + ]: 13108 : if (root->plan_params)
262 [ # # ]: 0 : elog(ERROR, "unexpected outer reference in set operation subquery");
263 : :
264 : : /* Figure out the appropriate target list for this subquery. */
265 : 13108 : tlist = generate_setop_tlist(colTypes, colCollations,
266 : 13108 : rtr->rtindex,
267 : : true,
268 : : subroot->processed_tlist,
269 : : refnames_tlist,
270 : : &trivial_tlist);
271 : 13108 : rel->reltarget = create_pathtarget(root, tlist);
272 : :
273 : : /* Return the fully-fledged tlist to caller, too */
274 : 13108 : *pTargetList = tlist;
275 : 13108 : *istrivial_tlist = trivial_tlist;
276 : : }
277 [ + - ]: 4478 : else if (IsA(setOp, SetOperationStmt))
278 : : {
279 : 4478 : SetOperationStmt *op = (SetOperationStmt *) setOp;
280 : :
281 : : /* UNIONs are much different from INTERSECT/EXCEPT */
282 [ + + ]: 4478 : if (op->op == SETOP_UNION)
283 : 3766 : rel = generate_union_paths(op, root,
284 : : refnames_tlist,
285 : : pTargetList);
286 : : else
287 : 712 : rel = generate_nonunion_paths(op, root,
288 : : refnames_tlist,
289 : : pTargetList);
290 : :
291 : : /*
292 : : * If necessary, add a Result node to project the caller-requested
293 : : * output columns.
294 : : *
295 : : * XXX you don't really want to know about this: setrefs.c will apply
296 : : * fix_upper_expr() to the Result node's tlist. This would fail if the
297 : : * Vars generated by generate_setop_tlist() were not exactly equal()
298 : : * to the corresponding tlist entries of the subplan. However, since
299 : : * the subplan was generated by generate_union_paths() or
300 : : * generate_nonunion_paths(), and hence its tlist was generated by
301 : : * generate_append_tlist() or generate_setop_tlist(), this will work.
302 : : * We just tell generate_setop_tlist() to use varno 0.
303 : : */
304 [ + + ]: 4478 : if (!tlist_same_datatypes(*pTargetList, colTypes, false) ||
305 [ - + ]: 4458 : !tlist_same_collations(*pTargetList, colCollations, false))
306 : : {
307 : : PathTarget *target;
308 : : bool trivial_tlist;
309 : : ListCell *lc;
310 : :
311 : 20 : *pTargetList = generate_setop_tlist(colTypes, colCollations,
312 : : 0,
313 : : false,
314 : : *pTargetList,
315 : : refnames_tlist,
316 : : &trivial_tlist);
317 : 20 : *istrivial_tlist = trivial_tlist;
318 : 20 : target = create_pathtarget(root, *pTargetList);
319 : :
320 : : /* Apply projection to each path */
321 [ + - + + : 40 : foreach(lc, rel->pathlist)
+ + ]
322 : : {
323 : 20 : Path *subpath = (Path *) lfirst(lc);
324 : : Path *path;
325 : :
326 : : Assert(subpath->param_info == NULL);
327 : 20 : path = apply_projection_to_path(root, subpath->parent,
328 : : subpath, target);
329 : : /* If we had to add a Result, path is different from subpath */
330 [ + + ]: 20 : if (path != subpath)
331 : 15 : lfirst(lc) = path;
332 : : }
333 : :
334 : : /* Apply projection to each partial path */
335 [ - + - - : 20 : foreach(lc, rel->partial_pathlist)
- + ]
336 : : {
337 : 0 : Path *subpath = (Path *) lfirst(lc);
338 : : Path *path;
339 : :
340 : : Assert(subpath->param_info == NULL);
341 : :
342 : : /* avoid apply_projection_to_path, in case of multiple refs */
343 : 0 : path = (Path *) create_projection_path(root, subpath->parent,
344 : : subpath, target);
345 : 0 : lfirst(lc) = path;
346 : : }
347 : : }
348 : 4478 : postprocess_setop_rel(root, rel);
349 : : }
350 : : else
351 : : {
352 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
353 : : (int) nodeTag(setOp));
354 : : *pTargetList = NIL;
355 : : rel = NULL; /* keep compiler quiet */
356 : : }
357 : :
358 : 17586 : return rel;
359 : : }
360 : :
361 : : /*
362 : : * Generate paths for a recursive UNION node
363 : : */
364 : : static RelOptInfo *
365 : 697 : generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root,
366 : : List *refnames_tlist,
367 : : List **pTargetList)
368 : : {
369 : : RelOptInfo *result_rel;
370 : : Path *path;
371 : : RelOptInfo *lrel,
372 : : *rrel;
373 : : Path *lpath;
374 : : Path *rpath;
375 : : List *lpath_tlist;
376 : : bool lpath_trivial_tlist;
377 : : List *rpath_tlist;
378 : : bool rpath_trivial_tlist;
379 : : List *tlist;
380 : : List *groupList;
381 : : double dNumGroups;
382 : :
383 : : /* Parser should have rejected other cases */
384 [ - + ]: 697 : if (setOp->op != SETOP_UNION)
385 [ # # ]: 0 : elog(ERROR, "only UNION queries can be recursive");
386 : : /* Worktable ID should be assigned */
387 : : Assert(root->wt_param_id >= 0);
388 : :
389 : : /*
390 : : * Unlike a regular UNION node, process the left and right inputs
391 : : * separately without any intention of combining them into one Append.
392 : : */
393 : 697 : lrel = recurse_set_operations(setOp->larg, root,
394 : : NULL, /* no value in sorted results */
395 : : setOp->colTypes, setOp->colCollations,
396 : : refnames_tlist,
397 : : &lpath_tlist,
398 : : &lpath_trivial_tlist);
399 [ + - ]: 697 : if (lrel->rtekind == RTE_SUBQUERY)
400 : 697 : build_setop_child_paths(root, lrel, lpath_trivial_tlist, lpath_tlist,
401 : : NIL, NULL);
402 : 697 : lpath = lrel->cheapest_total_path;
403 : : /* The right path will want to look at the left one ... */
404 : 697 : root->non_recursive_path = lpath;
405 : 697 : rrel = recurse_set_operations(setOp->rarg, root,
406 : : NULL, /* no value in sorted results */
407 : : setOp->colTypes, setOp->colCollations,
408 : : refnames_tlist,
409 : : &rpath_tlist,
410 : : &rpath_trivial_tlist);
411 [ + + ]: 697 : if (rrel->rtekind == RTE_SUBQUERY)
412 : 692 : build_setop_child_paths(root, rrel, rpath_trivial_tlist, rpath_tlist,
413 : : NIL, NULL);
414 : 697 : rpath = rrel->cheapest_total_path;
415 : 697 : root->non_recursive_path = NULL;
416 : :
417 : : /*
418 : : * Generate tlist for RecursiveUnion path node --- same as in Append cases
419 : : */
420 : 697 : tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations,
421 : : list_make2(lpath_tlist, rpath_tlist),
422 : : refnames_tlist);
423 : :
424 : 697 : *pTargetList = tlist;
425 : :
426 : : /* Build result relation. */
427 : 697 : result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
428 : 697 : bms_union(lrel->relids, rrel->relids));
429 : 697 : result_rel->reltarget = create_pathtarget(root, tlist);
430 : :
431 : : /*
432 : : * If UNION, identify the grouping operators
433 : : */
434 [ + + ]: 697 : if (setOp->all)
435 : : {
436 : 476 : groupList = NIL;
437 : 476 : dNumGroups = 0;
438 : : }
439 : : else
440 : : {
441 : : /* Identify the grouping semantics */
442 : 221 : groupList = generate_setop_grouplist(setOp, tlist);
443 : :
444 : : /* We only support hashing here */
445 [ + + ]: 221 : if (!grouping_is_hashable(groupList))
446 [ + - ]: 4 : ereport(ERROR,
447 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
448 : : errmsg("could not implement recursive UNION"),
449 : : errdetail("All column datatypes must be hashable.")));
450 : :
451 : : /*
452 : : * For the moment, take the number of distinct groups as equal to the
453 : : * total input size, ie, the worst case.
454 : : */
455 : 217 : dNumGroups = lpath->rows + rpath->rows * 10;
456 : : }
457 : :
458 : : /*
459 : : * And make the path node.
460 : : */
461 : 693 : path = (Path *) create_recursiveunion_path(root,
462 : : result_rel,
463 : : lpath,
464 : : rpath,
465 : 693 : result_rel->reltarget,
466 : : groupList,
467 : : root->wt_param_id,
468 : : dNumGroups);
469 : :
470 : 693 : add_path(result_rel, path);
471 : 693 : postprocess_setop_rel(root, result_rel);
472 : 693 : return result_rel;
473 : : }
474 : :
475 : : /*
476 : : * build_setop_child_paths
477 : : * Build paths for the set op child relation denoted by 'rel'.
478 : : *
479 : : * 'rel' is an RTE_SUBQUERY relation. We have already generated paths within
480 : : * the subquery's subroot; the task here is to create SubqueryScan paths for
481 : : * 'rel', representing scans of the useful subquery paths.
482 : : *
483 : : * interesting_pathkeys: if not NIL, also include paths that suit these
484 : : * pathkeys, sorting any unsorted paths as required.
485 : : * *pNumGroups: if not NULL, we estimate the number of distinct groups
486 : : * in the result, and store it there.
487 : : */
488 : : static void
489 : 13108 : build_setop_child_paths(PlannerInfo *root, RelOptInfo *rel,
490 : : bool trivial_tlist, List *child_tlist,
491 : : List *interesting_pathkeys, double *pNumGroups)
492 : : {
493 : : RelOptInfo *final_rel;
494 : 13108 : List *setop_pathkeys = rel->subroot->setop_pathkeys;
495 : : ListCell *lc;
496 : :
497 : : /* it can't be a set op child rel if it's not a subquery */
498 : : Assert(rel->rtekind == RTE_SUBQUERY);
499 : :
500 : : /* when sorting is needed, add child rel equivalences */
501 [ + + ]: 13108 : if (interesting_pathkeys != NIL)
502 : 10382 : add_setop_child_rel_equivalences(root,
503 : : rel,
504 : : child_tlist,
505 : : interesting_pathkeys);
506 : :
507 : : /*
508 : : * Mark rel with estimated output rows, width, etc. Note that we have to
509 : : * do this before generating outer-query paths, else cost_subqueryscan is
510 : : * not happy.
511 : : */
512 : 13108 : set_subquery_size_estimates(root, rel);
513 : :
514 : : /*
515 : : * Since we may want to add a partial path to this relation, we must set
516 : : * its consider_parallel flag correctly.
517 : : */
518 : 13108 : final_rel = fetch_upper_rel(rel->subroot, UPPERREL_FINAL, NULL);
519 : 13108 : rel->consider_parallel = final_rel->consider_parallel;
520 : :
521 : : /* Generate subquery scan paths for any interesting path in final_rel */
522 [ + - + + : 34150 : foreach(lc, final_rel->pathlist)
+ + ]
523 : : {
524 : 21042 : Path *subpath = (Path *) lfirst(lc);
525 : : List *pathkeys;
526 : 21042 : Path *cheapest_input_path = final_rel->cheapest_total_path;
527 : : bool is_sorted;
528 : : int presorted_keys;
529 : :
530 : : /* If the input rel is dummy, propagate that to this query level */
531 [ + + ]: 21042 : if (is_dummy_rel(final_rel))
532 : : {
533 : 88 : mark_dummy_rel(rel);
534 : 2842 : continue;
535 : : }
536 : :
537 : : /*
538 : : * Include the cheapest path as-is so that the set operation can be
539 : : * cheaply implemented using a method which does not require the input
540 : : * to be sorted.
541 : : */
542 [ + + ]: 20954 : if (subpath == cheapest_input_path)
543 : : {
544 : : /* Convert subpath's pathkeys to outer representation */
545 : 13020 : pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
546 : : make_tlist_from_pathtarget(subpath->pathtarget));
547 : :
548 : : /* Generate outer path using this subpath */
549 : 13020 : add_path(rel, (Path *) create_subqueryscan_path(root,
550 : : rel,
551 : : subpath,
552 : : trivial_tlist,
553 : : pathkeys,
554 : : NULL));
555 : : }
556 : :
557 : : /* skip dealing with sorted paths if the setop doesn't need them */
558 [ + + ]: 20954 : if (interesting_pathkeys == NIL)
559 : 2744 : continue;
560 : :
561 : : /*
562 : : * Create paths to suit final sort order required for setop_pathkeys.
563 : : * Here we'll sort the cheapest input path (if not sorted already) and
564 : : * incremental sort any paths which are partially sorted.
565 : : */
566 : 18210 : is_sorted = pathkeys_count_contained_in(setop_pathkeys,
567 : : subpath->pathkeys,
568 : : &presorted_keys);
569 : :
570 [ + + ]: 18210 : if (!is_sorted)
571 : : {
572 : 12124 : double limittuples = rel->subroot->limit_tuples;
573 : :
574 : : /*
575 : : * Try at least sorting the cheapest path and also try
576 : : * incrementally sorting any path which is partially sorted
577 : : * already (no need to deal with paths which have presorted keys
578 : : * when incremental sort is disabled unless it's the cheapest
579 : : * input path).
580 : : */
581 [ + + ]: 12124 : if (subpath != cheapest_input_path &&
582 [ + + - + ]: 2646 : (presorted_keys == 0 || !enable_incremental_sort))
583 : 10 : continue;
584 : :
585 : : /*
586 : : * We've no need to consider both a sort and incremental sort.
587 : : * We'll just do a sort if there are no presorted keys and an
588 : : * incremental sort when there are presorted keys.
589 : : */
590 [ + + + + ]: 12114 : if (presorted_keys == 0 || !enable_incremental_sort)
591 : 9478 : subpath = (Path *) create_sort_path(rel->subroot,
592 : : final_rel,
593 : : subpath,
594 : : setop_pathkeys,
595 : : limittuples);
596 : : else
597 : 2636 : subpath = (Path *) create_incremental_sort_path(rel->subroot,
598 : : final_rel,
599 : : subpath,
600 : : setop_pathkeys,
601 : : presorted_keys,
602 : : limittuples);
603 : : }
604 : :
605 : : /*
606 : : * subpath is now sorted, so add it to the pathlist. We already added
607 : : * the cheapest_input_path above, so don't add it again unless we just
608 : : * sorted it.
609 : : */
610 [ + + ]: 18200 : if (subpath != cheapest_input_path)
611 : : {
612 : : /* Convert subpath's pathkeys to outer representation */
613 : 17376 : pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
614 : : make_tlist_from_pathtarget(subpath->pathtarget));
615 : :
616 : : /* Generate outer path using this subpath */
617 : 17376 : add_path(rel, (Path *) create_subqueryscan_path(root,
618 : : rel,
619 : : subpath,
620 : : trivial_tlist,
621 : : pathkeys,
622 : : NULL));
623 : : }
624 : : }
625 : :
626 : : /* if consider_parallel is false, there should be no partial paths */
627 : : Assert(final_rel->consider_parallel ||
628 : : final_rel->partial_pathlist == NIL);
629 : :
630 : : /*
631 : : * If we have a partial path for the child relation, we can use that to
632 : : * build a partial path for this relation. But there's no point in
633 : : * considering any path but the cheapest.
634 : : */
635 [ + + + - ]: 13108 : if (rel->consider_parallel && bms_is_empty(rel->lateral_relids) &&
636 [ + + ]: 9021 : final_rel->partial_pathlist != NIL)
637 : : {
638 : : Path *partial_subpath;
639 : : Path *partial_path;
640 : :
641 : 10 : partial_subpath = linitial(final_rel->partial_pathlist);
642 : : partial_path = (Path *)
643 : 10 : create_subqueryscan_path(root, rel, partial_subpath,
644 : : trivial_tlist,
645 : : NIL, NULL);
646 : 10 : add_partial_path(rel, partial_path);
647 : : }
648 : :
649 : 13108 : postprocess_setop_rel(root, rel);
650 : :
651 : : /*
652 : : * Estimate number of groups if caller wants it. If the subquery used
653 : : * grouping or aggregation, its output is probably mostly unique anyway;
654 : : * otherwise do statistical estimation.
655 : : *
656 : : * XXX you don't really want to know about this: we do the estimation
657 : : * using the subroot->parse's original targetlist expressions, not the
658 : : * subroot->processed_tlist which might seem more appropriate. The reason
659 : : * is that if the subquery is itself a setop, it may return a
660 : : * processed_tlist containing "varno 0" Vars generated by
661 : : * generate_append_tlist, and those would confuse estimate_num_groups
662 : : * mightily. We ought to get rid of the "varno 0" hack, but that requires
663 : : * a redesign of the parsetree representation of setops, so that there can
664 : : * be an RTE corresponding to each setop's output. Note, we use this not
665 : : * subquery's targetlist but subroot->parse's targetlist, because it was
666 : : * revised by self-join removal. subquery's targetlist might contain the
667 : : * references to the removed relids.
668 : : */
669 [ + + ]: 13108 : if (pNumGroups)
670 : : {
671 : 10696 : PlannerInfo *subroot = rel->subroot;
672 : 10696 : Query *subquery = subroot->parse;
673 : :
674 [ + - + - ]: 10696 : if (subquery->groupClause || subquery->groupingSets ||
675 [ + + + - ]: 10696 : subquery->distinctClause || subroot->hasHavingQual ||
676 [ + + ]: 10686 : subquery->hasAggs)
677 : 12 : *pNumGroups = rel->cheapest_total_path->rows;
678 : : else
679 : 10684 : *pNumGroups = estimate_num_groups(subroot,
680 : 10684 : get_tlist_exprs(subroot->parse->targetList, false),
681 : 10684 : rel->cheapest_total_path->rows,
682 : : NULL,
683 : : NULL);
684 : : }
685 : 13108 : }
686 : :
687 : : /*
688 : : * Generate paths for a UNION or UNION ALL node
689 : : */
690 : : static RelOptInfo *
691 : 3766 : generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
692 : : List *refnames_tlist,
693 : : List **pTargetList)
694 : : {
695 : 3766 : Relids relids = NULL;
696 : : RelOptInfo *result_rel;
697 : : ListCell *lc;
698 : : ListCell *lc2;
699 : : ListCell *lc3;
700 : 3766 : AppendPathInput cheapest = {0};
701 : 3766 : AppendPathInput ordered = {0};
702 : 3766 : AppendPathInput partial = {0};
703 : 3766 : bool partial_paths_valid = true;
704 : 3766 : bool consider_parallel = true;
705 : : List *rellist;
706 : : List *tlist_list;
707 : : List *trivial_tlist_list;
708 : : List *tlist;
709 : 3766 : List *groupList = NIL;
710 : : Path *apath;
711 : 3766 : Path *gpath = NULL;
712 : 3766 : bool try_sorted = false;
713 : 3766 : List *union_pathkeys = NIL;
714 : 3766 : double dNumChildGroups = 0;
715 : :
716 : : /*
717 : : * If any of my children are identical UNION nodes (same op, all-flag, and
718 : : * colTypes/colCollations) then they can be merged into this node so that
719 : : * we generate only one Append/MergeAppend and unique-ification for the
720 : : * lot. Recurse to find such nodes.
721 : : */
722 : 3766 : rellist = plan_union_children(root,
723 : : op,
724 : : refnames_tlist,
725 : : &tlist_list,
726 : : &trivial_tlist_list);
727 : :
728 : : /*
729 : : * Generate tlist for Append/MergeAppend plan node.
730 : : *
731 : : * The tlist for an Append plan isn't important as far as the Append is
732 : : * concerned, but we must make it look real anyway for the benefit of the
733 : : * next plan level up.
734 : : */
735 : 3766 : tlist = generate_append_tlist(op->colTypes, op->colCollations,
736 : : tlist_list, refnames_tlist);
737 : 3766 : *pTargetList = tlist;
738 : :
739 : : /* For UNIONs (not UNION ALL), try sorting, if sorting is possible */
740 [ + + ]: 3766 : if (!op->all)
741 : : {
742 : : /* Identify the grouping semantics */
743 : 3306 : groupList = generate_setop_grouplist(op, tlist);
744 : :
745 [ + + ]: 3306 : if (grouping_is_sortable(op->groupClauses))
746 : : {
747 : 3244 : try_sorted = true;
748 : : /* Determine the pathkeys for sorting by the whole target list */
749 : 3244 : union_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
750 : : tlist);
751 : :
752 : 3244 : root->query_pathkeys = union_pathkeys;
753 : : }
754 : : }
755 : :
756 : : /*
757 : : * Now that we've got the append target list, we can build the union child
758 : : * paths.
759 : : */
760 [ + - + + : 14226 : forthree(lc, rellist, lc2, trivial_tlist_list, lc3, tlist_list)
+ - + + +
- + + + +
+ - + - +
+ ]
761 : : {
762 : 10460 : RelOptInfo *rel = lfirst(lc);
763 : 10460 : bool trivial_tlist = lfirst_int(lc2);
764 : 10460 : List *child_tlist = lfirst_node(List, lc3);
765 : 10460 : double childGroups = 0;
766 : :
767 : : /* only build paths for the union children */
768 [ + + ]: 10460 : if (rel->rtekind == RTE_SUBQUERY)
769 : 10330 : build_setop_child_paths(root, rel, trivial_tlist, child_tlist,
770 : : union_pathkeys,
771 [ + + ]: 10330 : op->all ? NULL : &childGroups);
772 : : else
773 : 130 : childGroups = rel->rows;
774 : :
775 : : /*
776 : : * For UNION (not UNION ALL), accumulate the per-child distinct-group
777 : : * estimates. This sum is the basis for the UNION's output estimate
778 : : * below: since distinct(A union B) <= distinct(A) + distinct(B), the
779 : : * union cannot have more distinct rows than its children do in total.
780 : : * Children that are known to be empty contribute nothing, so skip
781 : : * them.
782 : : */
783 [ + + + + ]: 10460 : if (!op->all && !is_dummy_rel(rel))
784 : 9287 : dNumChildGroups += childGroups;
785 : : }
786 : :
787 : : /* Build path lists and relid set. */
788 [ + - + + : 14226 : foreach(lc, rellist)
+ + ]
789 : : {
790 : 10460 : RelOptInfo *rel = lfirst(lc);
791 : : Path *ordered_path;
792 : :
793 : : /*
794 : : * Record the relids so that we can identify the correct
795 : : * UPPERREL_SETOP RelOptInfo below.
796 : : */
797 : 10460 : relids = bms_add_members(relids, rel->relids);
798 : :
799 : : /* Skip any UNION children that are proven not to yield any rows */
800 [ + + ]: 10460 : if (is_dummy_rel(rel))
801 : 48 : continue;
802 : :
803 : 20824 : cheapest.subpaths = lappend(cheapest.subpaths,
804 : 10412 : rel->cheapest_total_path);
805 : :
806 [ + + ]: 10412 : if (try_sorted)
807 : : {
808 : 3538 : ordered_path = get_cheapest_path_for_pathkeys(rel->pathlist,
809 : : union_pathkeys,
810 : : NULL,
811 : : TOTAL_COST,
812 : : false);
813 : :
814 [ + + ]: 3538 : if (ordered_path != NULL)
815 : 583 : ordered.subpaths = lappend(ordered.subpaths, ordered_path);
816 : : else
817 : : {
818 : : /*
819 : : * If we can't find a sorted path, just give up trying to
820 : : * generate a list of correctly sorted child paths. This can
821 : : * happen when type coercion was added to the targetlist due
822 : : * to mismatching types from the union children.
823 : : */
824 : 2955 : try_sorted = false;
825 : : }
826 : : }
827 : :
828 [ + + ]: 10412 : if (consider_parallel)
829 : : {
830 [ + + ]: 7469 : if (!rel->consider_parallel)
831 : : {
832 : 2835 : consider_parallel = false;
833 : 2835 : partial_paths_valid = false;
834 : : }
835 [ + + ]: 4634 : else if (rel->partial_pathlist == NIL)
836 : 4624 : partial_paths_valid = false;
837 : : else
838 : 10 : partial.partial_subpaths = lappend(partial.partial_subpaths,
839 : 10 : linitial(rel->partial_pathlist));
840 : : }
841 : : }
842 : :
843 : : /* Build result relation. */
844 : 3766 : result_rel = fetch_upper_rel(root, UPPERREL_SETOP, relids);
845 : 3766 : result_rel->reltarget = create_setop_pathtarget(root, tlist,
846 : : cheapest.subpaths);
847 : 3766 : result_rel->consider_parallel = consider_parallel;
848 : 3766 : result_rel->consider_startup = (root->tuple_fraction > 0);
849 : :
850 : : /* If all UNION children were dummy rels, make the resulting rel dummy */
851 [ + + ]: 3766 : if (cheapest.subpaths == NIL)
852 : : {
853 : 5 : mark_dummy_rel(result_rel);
854 : :
855 : 5 : return result_rel;
856 : : }
857 : :
858 : : /*
859 : : * Append the child results together using the cheapest paths from each
860 : : * union child.
861 : : */
862 : 3761 : apath = (Path *) create_append_path(root, result_rel, cheapest,
863 : : NIL, NULL, 0, false, -1);
864 : :
865 : : /*
866 : : * Although we told create_append_path to assign NIL pathkeys to the
867 : : * AppendPath, it may have overridden that (if there's just one surviving
868 : : * child path, it will use that path's pathkeys). However, createplan.c
869 : : * will fail because the append relation's tlist contains "varno 0" Vars
870 : : * (cf. generate_append_tlist), which won't match what is in the pathkeys.
871 : : * We need to fix that someday, but for now, just force the AppendPath's
872 : : * pathkeys back to NIL.
873 : : */
874 : 3761 : apath->pathkeys = NIL;
875 : :
876 : : /*
877 : : * Initialize the result row estimate to the total input size. This is
878 : : * correct for UNION ALL; for the UNION case it is overwritten below with
879 : : * the estimated number of distinct groups.
880 : : */
881 : 3761 : result_rel->rows = apath->rows;
882 : :
883 : : /*
884 : : * Now consider doing the same thing using the partial paths plus Append
885 : : * plus Gather.
886 : : */
887 [ + + ]: 3761 : if (partial_paths_valid)
888 : : {
889 : : Path *papath;
890 : 5 : int parallel_workers = 0;
891 : :
892 : : /* Find the highest number of workers requested for any subpath. */
893 [ + - + + : 15 : foreach(lc, partial.partial_subpaths)
+ + ]
894 : : {
895 : 10 : Path *subpath = lfirst(lc);
896 : :
897 : 10 : parallel_workers = Max(parallel_workers,
898 : : subpath->parallel_workers);
899 : : }
900 : : Assert(parallel_workers > 0);
901 : :
902 : : /*
903 : : * If the use of parallel append is permitted, always request at least
904 : : * log2(# of children) paths. We assume it can be useful to have
905 : : * extra workers in this case because they will be spread out across
906 : : * the children. The precise formula is just a guess; see
907 : : * add_paths_to_append_rel.
908 : : */
909 [ + - ]: 5 : if (enable_parallel_append)
910 : : {
911 [ + - ]: 5 : parallel_workers = Max(parallel_workers,
912 : : pg_leftmost_one_pos32(list_length(partial.partial_subpaths)) + 1);
913 : 5 : parallel_workers = Min(parallel_workers,
914 : : max_parallel_workers_per_gather);
915 : : }
916 : : Assert(parallel_workers > 0);
917 : :
918 : : papath = (Path *)
919 : 5 : create_append_path(root, result_rel, partial,
920 : : NIL, NULL, parallel_workers,
921 : : enable_parallel_append, -1);
922 : : /* See comment above about pathkeys vs. "varno 0" Vars */
923 : 5 : papath->pathkeys = NIL;
924 : :
925 : : gpath = (Path *)
926 : 5 : create_gather_path(root, result_rel, papath,
927 : 5 : result_rel->reltarget, NULL, NULL);
928 : : }
929 : :
930 [ + + ]: 3761 : if (!op->all)
931 : : {
932 : 3301 : bool can_sort = grouping_is_sortable(groupList);
933 : 3301 : bool can_hash = grouping_is_hashable(groupList);
934 : :
935 : : /*
936 : : * result_rel->rows was initialized to the total input size above,
937 : : * which is the correct estimate for UNION ALL. A UNION removes
938 : : * duplicates, so override it with the estimated number of distinct
939 : : * groups.
940 : : */
941 : 3301 : result_rel->rows = dNumChildGroups;
942 : :
943 [ + + ]: 3301 : if (can_hash)
944 : : {
945 : : Path *path;
946 : :
947 : : /*
948 : : * Try a hash aggregate plan on 'apath'. This is the cheapest
949 : : * available path containing each append child.
950 : : */
951 : 3241 : path = (Path *) create_agg_path(root,
952 : : result_rel,
953 : : apath,
954 : 3241 : result_rel->reltarget,
955 : : AGG_HASHED,
956 : : AGGSPLIT_SIMPLE,
957 : : groupList,
958 : : NIL,
959 : : NULL,
960 : : dNumChildGroups);
961 : 3241 : add_path(result_rel, path);
962 : :
963 : : /* Try hash aggregate on the Gather path, if valid */
964 [ + + ]: 3241 : if (gpath != NULL)
965 : : {
966 : : /* Hashed aggregate plan --- no sort needed */
967 : 5 : path = (Path *) create_agg_path(root,
968 : : result_rel,
969 : : gpath,
970 : 5 : result_rel->reltarget,
971 : : AGG_HASHED,
972 : : AGGSPLIT_SIMPLE,
973 : : groupList,
974 : : NIL,
975 : : NULL,
976 : : dNumChildGroups);
977 : 5 : add_path(result_rel, path);
978 : : }
979 : : }
980 : :
981 [ + + ]: 3301 : if (can_sort)
982 : : {
983 : 3239 : Path *path = apath;
984 : :
985 : : /* Try Sort -> Unique on the Append path */
986 [ + + ]: 3239 : if (groupList != NIL)
987 : 3204 : path = (Path *) create_sort_path(root, result_rel, path,
988 : : make_pathkeys_for_sortclauses(root, groupList, tlist),
989 : : -1.0);
990 : :
991 : 3239 : path = (Path *) create_unique_path(root,
992 : : result_rel,
993 : : path,
994 : 3239 : list_length(path->pathkeys),
995 : : dNumChildGroups);
996 : :
997 : 3239 : add_path(result_rel, path);
998 : :
999 : : /* Try Sort -> Unique on the Gather path, if set */
1000 [ + + ]: 3239 : if (gpath != NULL)
1001 : : {
1002 : 5 : path = gpath;
1003 : :
1004 : 5 : path = (Path *) create_sort_path(root, result_rel, path,
1005 : : make_pathkeys_for_sortclauses(root, groupList, tlist),
1006 : : -1.0);
1007 : :
1008 : 5 : path = (Path *) create_unique_path(root,
1009 : : result_rel,
1010 : : path,
1011 : 5 : list_length(path->pathkeys),
1012 : : dNumChildGroups);
1013 : 5 : add_path(result_rel, path);
1014 : : }
1015 : : }
1016 : :
1017 : : /*
1018 : : * Try making a MergeAppend path if we managed to find a path with the
1019 : : * correct pathkeys in each union child query.
1020 : : */
1021 [ + + + + ]: 3301 : if (try_sorted && groupList != NIL)
1022 : : {
1023 : : Path *path;
1024 : :
1025 : 249 : path = (Path *) create_merge_append_path(root,
1026 : : result_rel,
1027 : : ordered.subpaths,
1028 : : NIL,
1029 : : union_pathkeys,
1030 : : NULL);
1031 : :
1032 : : /* and make the MergeAppend unique */
1033 : 249 : path = (Path *) create_unique_path(root,
1034 : : result_rel,
1035 : : path,
1036 : : list_length(tlist),
1037 : : dNumChildGroups);
1038 : :
1039 : 249 : add_path(result_rel, path);
1040 : : }
1041 : : }
1042 : : else
1043 : : {
1044 : : /* UNION ALL */
1045 : 460 : add_path(result_rel, apath);
1046 : :
1047 [ - + ]: 460 : if (gpath != NULL)
1048 : 0 : add_path(result_rel, gpath);
1049 : : }
1050 : :
1051 : 3761 : return result_rel;
1052 : : }
1053 : :
1054 : : /*
1055 : : * Generate paths for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node
1056 : : */
1057 : : static RelOptInfo *
1058 : 712 : generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
1059 : : List *refnames_tlist,
1060 : : List **pTargetList)
1061 : : {
1062 : : RelOptInfo *result_rel;
1063 : : RelOptInfo *lrel,
1064 : : *rrel;
1065 : 712 : double save_fraction = root->tuple_fraction;
1066 : : Path *lpath,
1067 : : *rpath,
1068 : : *path;
1069 : : List *lpath_tlist,
1070 : : *rpath_tlist,
1071 : : *tlist,
1072 : : *groupList;
1073 : : bool lpath_trivial_tlist,
1074 : : rpath_trivial_tlist,
1075 : : result_trivial_tlist;
1076 : 712 : List *nonunion_pathkeys = NIL;
1077 : : double dLeftGroups,
1078 : : dRightGroups,
1079 : : dNumGroups,
1080 : : dNumOutputRows;
1081 : : bool can_sort;
1082 : : bool can_hash;
1083 : : SetOpCmd cmd;
1084 : :
1085 : : /*
1086 : : * Tell children to fetch all tuples.
1087 : : */
1088 : 712 : root->tuple_fraction = 0.0;
1089 : :
1090 : : /* Recurse on children */
1091 : 712 : lrel = recurse_set_operations(op->larg, root,
1092 : : op,
1093 : : op->colTypes, op->colCollations,
1094 : : refnames_tlist,
1095 : : &lpath_tlist,
1096 : : &lpath_trivial_tlist);
1097 : :
1098 : 712 : rrel = recurse_set_operations(op->rarg, root,
1099 : : op,
1100 : : op->colTypes, op->colCollations,
1101 : : refnames_tlist,
1102 : : &rpath_tlist,
1103 : : &rpath_trivial_tlist);
1104 : :
1105 : : /*
1106 : : * Generate tlist for SetOp plan node.
1107 : : *
1108 : : * The tlist for a SetOp plan isn't important so far as the SetOp is
1109 : : * concerned, but we must make it look real anyway for the benefit of the
1110 : : * next plan level up.
1111 : : */
1112 : 712 : tlist = generate_setop_tlist(op->colTypes, op->colCollations,
1113 : : 0, false, lpath_tlist, refnames_tlist,
1114 : : &result_trivial_tlist);
1115 : :
1116 : : /* We should not have needed any type coercions in the tlist */
1117 : : Assert(result_trivial_tlist);
1118 : :
1119 : 712 : *pTargetList = tlist;
1120 : :
1121 : : /* Identify the grouping semantics */
1122 : 712 : groupList = generate_setop_grouplist(op, tlist);
1123 : :
1124 : : /* Check whether the operators support sorting or hashing */
1125 : 712 : can_sort = grouping_is_sortable(groupList);
1126 : 712 : can_hash = grouping_is_hashable(groupList);
1127 [ - + - - ]: 712 : if (!can_sort && !can_hash)
1128 [ # # # # ]: 0 : ereport(ERROR,
1129 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1130 : : /* translator: %s is INTERSECT or EXCEPT */
1131 : : errmsg("could not implement %s",
1132 : : (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT"),
1133 : : errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
1134 : :
1135 [ + - ]: 712 : if (can_sort)
1136 : : {
1137 : : /* Determine the pathkeys for sorting by the whole target list */
1138 : 712 : nonunion_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
1139 : : tlist);
1140 : :
1141 : 712 : root->query_pathkeys = nonunion_pathkeys;
1142 : : }
1143 : :
1144 : : /*
1145 : : * Now that we've got all that info, we can build the child paths.
1146 : : */
1147 [ + + ]: 712 : if (lrel->rtekind == RTE_SUBQUERY)
1148 : 687 : build_setop_child_paths(root, lrel, lpath_trivial_tlist, lpath_tlist,
1149 : : nonunion_pathkeys, &dLeftGroups);
1150 : : else
1151 : 25 : dLeftGroups = lrel->rows;
1152 [ + + ]: 712 : if (rrel->rtekind == RTE_SUBQUERY)
1153 : 702 : build_setop_child_paths(root, rrel, rpath_trivial_tlist, rpath_tlist,
1154 : : nonunion_pathkeys, &dRightGroups);
1155 : : else
1156 : 10 : dRightGroups = rrel->rows;
1157 : :
1158 : : /* Undo effects of forcing tuple_fraction to 0 */
1159 : 712 : root->tuple_fraction = save_fraction;
1160 : :
1161 : : /*
1162 : : * For EXCEPT, we must put the left input first. For INTERSECT, either
1163 : : * order should give the same results, and we prefer to put the smaller
1164 : : * input first in order to (a) minimize the size of the hash table in the
1165 : : * hashing case, and (b) improve our chances of exploiting the executor's
1166 : : * fast path for empty left-hand input. "Smaller" means the one with the
1167 : : * fewer groups.
1168 : : */
1169 [ + + + + ]: 712 : if (op->op != SETOP_EXCEPT && dLeftGroups > dRightGroups)
1170 : : {
1171 : : /* need to swap the two inputs */
1172 : : RelOptInfo *tmprel;
1173 : : List *tmplist;
1174 : : double tmpd;
1175 : :
1176 : 30 : tmprel = lrel;
1177 : 30 : lrel = rrel;
1178 : 30 : rrel = tmprel;
1179 : 30 : tmplist = lpath_tlist;
1180 : 30 : lpath_tlist = rpath_tlist;
1181 : 30 : rpath_tlist = tmplist;
1182 : 30 : tmpd = dLeftGroups;
1183 : 30 : dLeftGroups = dRightGroups;
1184 : 30 : dRightGroups = tmpd;
1185 : : }
1186 : :
1187 : 712 : lpath = lrel->cheapest_total_path;
1188 : 712 : rpath = rrel->cheapest_total_path;
1189 : :
1190 : : /* Build result relation. */
1191 : 712 : result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
1192 : 712 : bms_union(lrel->relids, rrel->relids));
1193 : :
1194 : : /*
1195 : : * Create the PathTarget and set the width accordingly. For EXCEPT, since
1196 : : * the set op result won't contain rows from the rpath, we only account
1197 : : * for the width of the lpath. For INTERSECT, use both input paths.
1198 : : */
1199 [ + + ]: 712 : if (op->op == SETOP_EXCEPT)
1200 : 452 : result_rel->reltarget = create_setop_pathtarget(root, tlist,
1201 : : list_make1(lpath));
1202 : : else
1203 : 260 : result_rel->reltarget = create_setop_pathtarget(root, tlist,
1204 : : list_make2(lpath, rpath));
1205 : :
1206 : : /* Check for provably empty setop inputs and add short-circuit paths. */
1207 [ + + ]: 712 : if (op->op == SETOP_EXCEPT)
1208 : : {
1209 : : /*
1210 : : * For EXCEPTs, if the left side is dummy then there's no need to
1211 : : * inspect the right-hand side as scanning the right to find tuples to
1212 : : * remove won't make the left-hand input any more empty.
1213 : : */
1214 [ + + ]: 452 : if (is_dummy_rel(lrel))
1215 : : {
1216 : 10 : mark_dummy_rel(result_rel);
1217 : :
1218 : 10 : return result_rel;
1219 : : }
1220 : :
1221 : : /* Handle EXCEPTs with dummy right input */
1222 [ + + ]: 442 : if (is_dummy_rel(rrel))
1223 : : {
1224 [ + + ]: 15 : if (op->all)
1225 : : {
1226 : : Path *apath;
1227 : 10 : AppendPathInput append = {0};
1228 : :
1229 : 10 : append.subpaths = list_make1(lpath);
1230 : :
1231 : : /*
1232 : : * EXCEPT ALL: If the right-hand input is dummy then we can
1233 : : * simply scan the left-hand input. To keep createplan.c
1234 : : * happy, use a single child Append to handle the translation
1235 : : * between the set op targetlist and the targetlist of the
1236 : : * left input. The Append will be removed in setrefs.c.
1237 : : */
1238 : 10 : apath = (Path *) create_append_path(root, result_rel,
1239 : : append, NIL, NULL, 0,
1240 : : false, -1);
1241 : : /* See comment in generate_union_paths about "varno 0" Vars */
1242 : 10 : apath->pathkeys = NIL;
1243 : :
1244 : 10 : add_path(result_rel, apath);
1245 : :
1246 : 10 : return result_rel;
1247 : : }
1248 : : else
1249 : : {
1250 : : /*
1251 : : * To make EXCEPT with a dummy RHS work means having to
1252 : : * deduplicate the left input. That could be done with
1253 : : * AggPaths, but it doesn't seem worth the effort. Let the
1254 : : * normal path generation code below handle this one.
1255 : : */
1256 : : }
1257 : : }
1258 : : }
1259 : : else
1260 : : {
1261 : : /*
1262 : : * For INTERSECT, if either input is a dummy rel then we can mark the
1263 : : * result_rel as dummy since intersecting with an empty relation can
1264 : : * never yield any results. This is true regardless of INTERSECT or
1265 : : * INTERSECT ALL.
1266 : : */
1267 [ + + - + ]: 260 : if (is_dummy_rel(lrel) || is_dummy_rel(rrel))
1268 : : {
1269 : 15 : mark_dummy_rel(result_rel);
1270 : :
1271 : 15 : return result_rel;
1272 : : }
1273 : : }
1274 : :
1275 : : /*
1276 : : * Estimate number of distinct groups that we'll need hashtable entries
1277 : : * for; this is the size of the left-hand input for EXCEPT, or the smaller
1278 : : * input for INTERSECT. Also estimate the number of eventual output rows.
1279 : : * In non-ALL cases, we estimate each group produces one output row; in
1280 : : * ALL cases use the relevant relation size. These are worst-case
1281 : : * estimates, of course, but we need to be conservative.
1282 : : */
1283 [ + + ]: 677 : if (op->op == SETOP_EXCEPT)
1284 : : {
1285 : 432 : dNumGroups = dLeftGroups;
1286 [ + + ]: 432 : dNumOutputRows = op->all ? lpath->rows : dNumGroups;
1287 : : }
1288 : : else
1289 : : {
1290 : 245 : dNumGroups = dLeftGroups;
1291 [ + + + + ]: 245 : dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
1292 : : }
1293 : 677 : result_rel->rows = dNumOutputRows;
1294 : :
1295 : : /* Select the SetOpCmd type */
1296 [ + + - ]: 677 : switch (op->op)
1297 : : {
1298 : 245 : case SETOP_INTERSECT:
1299 : 245 : cmd = op->all ? SETOPCMD_INTERSECT_ALL : SETOPCMD_INTERSECT;
1300 : 245 : break;
1301 : 432 : case SETOP_EXCEPT:
1302 [ + + ]: 432 : cmd = op->all ? SETOPCMD_EXCEPT_ALL : SETOPCMD_EXCEPT;
1303 : 432 : break;
1304 : 0 : default:
1305 [ # # ]: 0 : elog(ERROR, "unrecognized set op: %d", (int) op->op);
1306 : : cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */
1307 : : break;
1308 : : }
1309 : :
1310 : : /*
1311 : : * If we can hash, that just requires a SetOp atop the cheapest inputs.
1312 : : */
1313 [ + + ]: 677 : if (can_hash)
1314 : : {
1315 : 627 : path = (Path *) create_setop_path(root,
1316 : : result_rel,
1317 : : lpath,
1318 : : rpath,
1319 : : cmd,
1320 : : SETOP_HASHED,
1321 : : groupList,
1322 : : dNumGroups,
1323 : : dNumOutputRows);
1324 : 627 : add_path(result_rel, path);
1325 : : }
1326 : :
1327 : : /*
1328 : : * If we can sort, generate the cheapest sorted input paths, and add a
1329 : : * SetOp atop those.
1330 : : */
1331 [ + - ]: 677 : if (can_sort)
1332 : : {
1333 : : List *pathkeys;
1334 : : Path *slpath,
1335 : : *srpath;
1336 : :
1337 : : /* First the left input ... */
1338 : 677 : pathkeys = make_pathkeys_for_sortclauses(root,
1339 : : groupList,
1340 : : lpath_tlist);
1341 [ + + ]: 677 : if (pathkeys_contained_in(pathkeys, lpath->pathkeys))
1342 : 80 : slpath = lpath; /* cheapest path is already sorted */
1343 : : else
1344 : : {
1345 : 597 : slpath = get_cheapest_path_for_pathkeys(lrel->pathlist,
1346 : : nonunion_pathkeys,
1347 : : NULL,
1348 : : TOTAL_COST,
1349 : : false);
1350 : : /* Subquery failed to produce any presorted paths? */
1351 [ + + ]: 597 : if (slpath == NULL)
1352 : 216 : slpath = (Path *) create_sort_path(root,
1353 : : lpath->parent,
1354 : : lpath,
1355 : : pathkeys,
1356 : : -1.0);
1357 : : }
1358 : :
1359 : : /* and now the same for the right. */
1360 : 677 : pathkeys = make_pathkeys_for_sortclauses(root,
1361 : : groupList,
1362 : : rpath_tlist);
1363 [ + + ]: 677 : if (pathkeys_contained_in(pathkeys, rpath->pathkeys))
1364 : 90 : srpath = rpath; /* cheapest path is already sorted */
1365 : : else
1366 : : {
1367 : 587 : srpath = get_cheapest_path_for_pathkeys(rrel->pathlist,
1368 : : nonunion_pathkeys,
1369 : : NULL,
1370 : : TOTAL_COST,
1371 : : false);
1372 : : /* Subquery failed to produce any presorted paths? */
1373 [ + + ]: 587 : if (srpath == NULL)
1374 : 226 : srpath = (Path *) create_sort_path(root,
1375 : : rpath->parent,
1376 : : rpath,
1377 : : pathkeys,
1378 : : -1.0);
1379 : : }
1380 : :
1381 : 677 : path = (Path *) create_setop_path(root,
1382 : : result_rel,
1383 : : slpath,
1384 : : srpath,
1385 : : cmd,
1386 : : SETOP_SORTED,
1387 : : groupList,
1388 : : dNumGroups,
1389 : : dNumOutputRows);
1390 : 677 : add_path(result_rel, path);
1391 : : }
1392 : :
1393 : 677 : return result_rel;
1394 : : }
1395 : :
1396 : : /*
1397 : : * Pull up children of a UNION node that are identically-propertied UNIONs,
1398 : : * and perform planning of the queries underneath the N-way UNION.
1399 : : *
1400 : : * The result is a list of RelOptInfos containing Paths for sub-nodes, with
1401 : : * one entry for each descendant that is a leaf query or non-identical setop.
1402 : : * We also return parallel lists of the childrens' targetlists and
1403 : : * is-trivial-tlist flags.
1404 : : *
1405 : : * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct
1406 : : * output rows will be lost anyway.
1407 : : */
1408 : : static List *
1409 : 3766 : plan_union_children(PlannerInfo *root,
1410 : : SetOperationStmt *top_union,
1411 : : List *refnames_tlist,
1412 : : List **tlist_list,
1413 : : List **istrivial_tlist)
1414 : : {
1415 : 3766 : List *pending_rels = list_make1(top_union);
1416 : 3766 : List *result = NIL;
1417 : : List *child_tlist;
1418 : : bool trivial_tlist;
1419 : :
1420 : 3766 : *tlist_list = NIL;
1421 : 3766 : *istrivial_tlist = NIL;
1422 : :
1423 [ + + ]: 20920 : while (pending_rels != NIL)
1424 : : {
1425 : 17154 : Node *setOp = linitial(pending_rels);
1426 : :
1427 : 17154 : pending_rels = list_delete_first(pending_rels);
1428 : :
1429 [ + + ]: 17154 : if (IsA(setOp, SetOperationStmt))
1430 : : {
1431 : 6824 : SetOperationStmt *op = (SetOperationStmt *) setOp;
1432 : :
1433 [ + + ]: 6824 : if (op->op == top_union->op &&
1434 [ + + + + : 13443 : (op->all == top_union->all || op->all) &&
+ + ]
1435 [ + - ]: 13403 : equal(op->colTypes, top_union->colTypes) &&
1436 : 6694 : equal(op->colCollations, top_union->colCollations))
1437 : : {
1438 : : /* Same UNION, so fold children into parent */
1439 : 6694 : pending_rels = lcons(op->rarg, pending_rels);
1440 : 6694 : pending_rels = lcons(op->larg, pending_rels);
1441 : 6694 : continue;
1442 : : }
1443 : : }
1444 : :
1445 : : /*
1446 : : * Not same, so plan this child separately.
1447 : : *
1448 : : * If top_union isn't a UNION ALL, then we are interested in sorted
1449 : : * output from the child, so pass top_union as parentOp. Note that
1450 : : * this isn't necessarily the child node's immediate SetOperationStmt
1451 : : * parent, but that's fine: it's the effective parent.
1452 : : */
1453 : 10460 : result = lappend(result, recurse_set_operations(setOp, root,
1454 [ + + ]: 10460 : top_union->all ? NULL : top_union,
1455 : : top_union->colTypes,
1456 : : top_union->colCollations,
1457 : : refnames_tlist,
1458 : : &child_tlist,
1459 : : &trivial_tlist));
1460 : 10460 : *tlist_list = lappend(*tlist_list, child_tlist);
1461 : 10460 : *istrivial_tlist = lappend_int(*istrivial_tlist, trivial_tlist);
1462 : : }
1463 : :
1464 : 3766 : return result;
1465 : : }
1466 : :
1467 : : /*
1468 : : * postprocess_setop_rel - perform steps required after adding paths
1469 : : */
1470 : : static void
1471 : 18279 : postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
1472 : : {
1473 : : /*
1474 : : * We don't currently worry about allowing FDWs to contribute paths to
1475 : : * this relation, but give extensions a chance.
1476 : : */
1477 [ - + ]: 18279 : if (create_upper_paths_hook)
1478 : 0 : (*create_upper_paths_hook) (root, UPPERREL_SETOP,
1479 : : NULL, rel, NULL);
1480 : :
1481 : : /* Select cheapest path */
1482 : 18279 : set_cheapest(rel);
1483 : 18279 : }
1484 : :
1485 : : /*
1486 : : * Generate targetlist for a set-operation plan node
1487 : : *
1488 : : * colTypes: OID list of set-op's result column datatypes
1489 : : * colCollations: OID list of set-op's result column collations
1490 : : * varno: varno to use in generated Vars
1491 : : * hack_constants: true to copy up constants (see comments in code)
1492 : : * input_tlist: targetlist of this node's input node
1493 : : * refnames_tlist: targetlist to take column names from
1494 : : * trivial_tlist: output parameter, set to true if targetlist is trivial
1495 : : */
1496 : : static List *
1497 : 13840 : generate_setop_tlist(List *colTypes, List *colCollations,
1498 : : Index varno,
1499 : : bool hack_constants,
1500 : : List *input_tlist,
1501 : : List *refnames_tlist,
1502 : : bool *trivial_tlist)
1503 : : {
1504 : 13840 : List *tlist = NIL;
1505 : 13840 : int resno = 1;
1506 : : ListCell *ctlc,
1507 : : *cclc,
1508 : : *itlc,
1509 : : *rtlc;
1510 : : TargetEntry *tle;
1511 : : Node *expr;
1512 : :
1513 : 13840 : *trivial_tlist = true; /* until proven differently */
1514 : :
1515 [ + + + + : 55610 : forfour(ctlc, colTypes, cclc, colCollations,
+ + + + +
+ + + + +
+ + + + +
- + - + -
+ + ]
1516 : : itlc, input_tlist, rtlc, refnames_tlist)
1517 : : {
1518 : 41770 : Oid colType = lfirst_oid(ctlc);
1519 : 41770 : Oid colColl = lfirst_oid(cclc);
1520 : 41770 : TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
1521 : 41770 : TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
1522 : :
1523 : : Assert(inputtle->resno == resno);
1524 : : Assert(reftle->resno == resno);
1525 : : Assert(!inputtle->resjunk);
1526 : : Assert(!reftle->resjunk);
1527 : :
1528 : : /*
1529 : : * Generate columns referencing input columns and having appropriate
1530 : : * data types and column names. Insert datatype coercions where
1531 : : * necessary.
1532 : : *
1533 : : * HACK: constants in the input's targetlist are copied up as-is
1534 : : * rather than being referenced as subquery outputs. This is mainly
1535 : : * to ensure that when we try to coerce them to the output column's
1536 : : * datatype, the right things happen for UNKNOWN constants. But do
1537 : : * this only at the first level of subquery-scan plans; we don't want
1538 : : * phony constants appearing in the output tlists of upper-level
1539 : : * nodes!
1540 : : *
1541 : : * Note that copying a constant doesn't in itself require us to mark
1542 : : * the tlist nontrivial; see trivial_subqueryscan() in setrefs.c.
1543 : : */
1544 [ + + + - : 41770 : if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
+ + ]
1545 : 12680 : expr = (Node *) inputtle->expr;
1546 : : else
1547 : 116360 : expr = (Node *) makeVar(varno,
1548 : 29090 : inputtle->resno,
1549 : 29090 : exprType((Node *) inputtle->expr),
1550 : 29090 : exprTypmod((Node *) inputtle->expr),
1551 : 29090 : exprCollation((Node *) inputtle->expr),
1552 : : 0);
1553 : :
1554 [ + + ]: 41770 : if (exprType(expr) != colType)
1555 : : {
1556 : : /*
1557 : : * Note: it's not really cool to be applying coerce_to_common_type
1558 : : * here; one notable point is that assign_expr_collations never
1559 : : * gets run on any generated nodes. For the moment that's not a
1560 : : * problem because we force the correct exposed collation below.
1561 : : * It would likely be best to make the parser generate the correct
1562 : : * output tlist for every set-op to begin with, though.
1563 : : */
1564 : 953 : expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */
1565 : : expr,
1566 : : colType,
1567 : : "UNION/INTERSECT/EXCEPT");
1568 : 953 : *trivial_tlist = false; /* the coercion makes it not trivial */
1569 : : }
1570 : :
1571 : : /*
1572 : : * Ensure the tlist entry's exposed collation matches the set-op. This
1573 : : * is necessary because plan_set_operations() reports the result
1574 : : * ordering as a list of SortGroupClauses, which don't carry collation
1575 : : * themselves but just refer to tlist entries. If we don't show the
1576 : : * right collation then planner.c might do the wrong thing in
1577 : : * higher-level queries.
1578 : : *
1579 : : * Note we use RelabelType, not CollateExpr, since this expression
1580 : : * will reach the executor without any further processing.
1581 : : */
1582 [ + + ]: 41770 : if (exprCollation(expr) != colColl)
1583 : : {
1584 : 10805 : expr = applyRelabelType(expr,
1585 : : exprType(expr), exprTypmod(expr), colColl,
1586 : : COERCE_IMPLICIT_CAST, -1, false);
1587 : 10805 : *trivial_tlist = false; /* the relabel makes it not trivial */
1588 : : }
1589 : :
1590 : 83540 : tle = makeTargetEntry((Expr *) expr,
1591 : 41770 : (AttrNumber) resno++,
1592 : 41770 : pstrdup(reftle->resname),
1593 : : false);
1594 : :
1595 : : /*
1596 : : * By convention, all output columns in a setop tree have
1597 : : * ressortgroupref equal to their resno. In some cases the ref isn't
1598 : : * needed, but this is a cleaner way than modifying the tlist later.
1599 : : */
1600 : 41770 : tle->ressortgroupref = tle->resno;
1601 : :
1602 : 41770 : tlist = lappend(tlist, tle);
1603 : : }
1604 : :
1605 : 13840 : return tlist;
1606 : : }
1607 : :
1608 : : /*
1609 : : * Generate targetlist for a set-operation Append node
1610 : : *
1611 : : * colTypes: OID list of set-op's result column datatypes
1612 : : * colCollations: OID list of set-op's result column collations
1613 : : * input_tlists: list of tlists for sub-plans of the Append
1614 : : * refnames_tlist: targetlist to take column names from
1615 : : *
1616 : : * The entries in the Append's targetlist should always be simple Vars;
1617 : : * we just have to make sure they have the right datatypes/typmods/collations.
1618 : : * The Vars are always generated with varno 0.
1619 : : *
1620 : : * XXX a problem with the varno-zero approach is that set_pathtarget_cost_width
1621 : : * cannot figure out a realistic width for the tlist we make here. But we
1622 : : * ought to refactor this code to produce a PathTarget directly, anyway.
1623 : : */
1624 : : static List *
1625 : 4463 : generate_append_tlist(List *colTypes, List *colCollations,
1626 : : List *input_tlists,
1627 : : List *refnames_tlist)
1628 : : {
1629 : 4463 : List *tlist = NIL;
1630 : 4463 : int resno = 1;
1631 : : ListCell *curColType;
1632 : : ListCell *curColCollation;
1633 : : ListCell *ref_tl_item;
1634 : : int colindex;
1635 : : TargetEntry *tle;
1636 : : Node *expr;
1637 : : ListCell *tlistl;
1638 : : int32 *colTypmods;
1639 : :
1640 : : /*
1641 : : * First extract typmods to use.
1642 : : *
1643 : : * If the inputs all agree on type and typmod of a particular column, use
1644 : : * that typmod; else use -1.
1645 : : */
1646 : 4463 : colTypmods = palloc_array(int32, list_length(colTypes));
1647 : :
1648 [ + - + + : 16317 : foreach(tlistl, input_tlists)
+ + ]
1649 : : {
1650 : 11854 : List *subtlist = (List *) lfirst(tlistl);
1651 : : ListCell *subtlistl;
1652 : :
1653 : 11854 : curColType = list_head(colTypes);
1654 : 11854 : colindex = 0;
1655 [ + + + + : 45102 : foreach(subtlistl, subtlist)
+ + ]
1656 : : {
1657 : 33248 : TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
1658 : :
1659 : : Assert(!subtle->resjunk);
1660 : : Assert(curColType != NULL);
1661 [ + - ]: 33248 : if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1662 : : {
1663 : : /* If first subplan, copy the typmod; else compare */
1664 : 33248 : int32 subtypmod = exprTypmod((Node *) subtle->expr);
1665 : :
1666 [ + + ]: 33248 : if (tlistl == list_head(input_tlists))
1667 : 11830 : colTypmods[colindex] = subtypmod;
1668 [ + + ]: 21418 : else if (subtypmod != colTypmods[colindex])
1669 : 10 : colTypmods[colindex] = -1;
1670 : : }
1671 : : else
1672 : : {
1673 : : /* types disagree, so force typmod to -1 */
1674 : 0 : colTypmods[colindex] = -1;
1675 : : }
1676 : 33248 : curColType = lnext(colTypes, curColType);
1677 : 33248 : colindex++;
1678 : : }
1679 : : Assert(curColType == NULL);
1680 : : }
1681 : :
1682 : : /*
1683 : : * Now we can build the tlist for the Append.
1684 : : */
1685 : 4463 : colindex = 0;
1686 [ + + + + : 16293 : forthree(curColType, colTypes, curColCollation, colCollations,
+ + + + +
+ + + + +
+ - + - +
+ ]
1687 : : ref_tl_item, refnames_tlist)
1688 : : {
1689 : 11830 : Oid colType = lfirst_oid(curColType);
1690 : 11830 : int32 colTypmod = colTypmods[colindex++];
1691 : 11830 : Oid colColl = lfirst_oid(curColCollation);
1692 : 11830 : TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
1693 : :
1694 : : Assert(reftle->resno == resno);
1695 : : Assert(!reftle->resjunk);
1696 : 11830 : expr = (Node *) makeVar(0,
1697 : : resno,
1698 : : colType,
1699 : : colTypmod,
1700 : : colColl,
1701 : : 0);
1702 : 23660 : tle = makeTargetEntry((Expr *) expr,
1703 : 11830 : (AttrNumber) resno++,
1704 : 11830 : pstrdup(reftle->resname),
1705 : : false);
1706 : :
1707 : : /*
1708 : : * By convention, all output columns in a setop tree have
1709 : : * ressortgroupref equal to their resno. In some cases the ref isn't
1710 : : * needed, but this is a cleaner way than modifying the tlist later.
1711 : : */
1712 : 11830 : tle->ressortgroupref = tle->resno;
1713 : :
1714 : 11830 : tlist = lappend(tlist, tle);
1715 : : }
1716 : :
1717 : 4463 : pfree(colTypmods);
1718 : :
1719 : 4463 : return tlist;
1720 : : }
1721 : :
1722 : : /*
1723 : : * generate_setop_grouplist
1724 : : * Build a SortGroupClause list defining the sort/grouping properties
1725 : : * of the setop's output columns.
1726 : : *
1727 : : * Parse analysis already determined the properties and built a suitable
1728 : : * list, except that the entries do not have sortgrouprefs set because
1729 : : * the parser output representation doesn't include a tlist for each
1730 : : * setop. So what we need to do here is copy that list and install
1731 : : * proper sortgrouprefs into it (copying those from the targetlist).
1732 : : */
1733 : : static List *
1734 : 4239 : generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
1735 : : {
1736 : 4239 : List *grouplist = copyObject(op->groupClauses);
1737 : : ListCell *lg;
1738 : : ListCell *lt;
1739 : :
1740 : 4239 : lg = list_head(grouplist);
1741 [ + + + + : 16594 : foreach(lt, targetlist)
+ + ]
1742 : : {
1743 : 12355 : TargetEntry *tle = (TargetEntry *) lfirst(lt);
1744 : : SortGroupClause *sgc;
1745 : :
1746 : : Assert(!tle->resjunk);
1747 : :
1748 : : /* non-resjunk columns should have sortgroupref = resno */
1749 : : Assert(tle->ressortgroupref == tle->resno);
1750 : :
1751 : : /* non-resjunk columns should have grouping clauses */
1752 : : Assert(lg != NULL);
1753 : 12355 : sgc = (SortGroupClause *) lfirst(lg);
1754 : 12355 : lg = lnext(grouplist, lg);
1755 : : Assert(sgc->tleSortGroupRef == 0);
1756 : :
1757 : 12355 : sgc->tleSortGroupRef = tle->ressortgroupref;
1758 : : }
1759 : : Assert(lg == NULL);
1760 : 4239 : return grouplist;
1761 : : }
1762 : :
1763 : : /*
1764 : : * create_setop_pathtarget
1765 : : * Do the normal create_pathtarget() work, plus set the resulting
1766 : : * PathTarget's width to the average width of the Paths in child_pathlist
1767 : : * weighted using the estimated row count of each path.
1768 : : *
1769 : : * Note: This is required because set op target lists use varno==0, which
1770 : : * results in a type default width estimate rather than one that's based on
1771 : : * statistics of the columns from the set op children.
1772 : : */
1773 : : static PathTarget *
1774 : 4478 : create_setop_pathtarget(PlannerInfo *root, List *tlist, List *child_pathlist)
1775 : : {
1776 : : PathTarget *reltarget;
1777 : : ListCell *lc;
1778 : 4478 : double parent_rows = 0;
1779 : 4478 : double parent_size = 0;
1780 : :
1781 : 4478 : reltarget = create_pathtarget(root, tlist);
1782 : :
1783 : : /* Calculate the total rows and total size. */
1784 [ + + + + : 15862 : foreach(lc, child_pathlist)
+ + ]
1785 : : {
1786 : 11384 : Path *path = (Path *) lfirst(lc);
1787 : :
1788 : 11384 : parent_rows += path->rows;
1789 : 11384 : parent_size += path->parent->reltarget->width * path->rows;
1790 : : }
1791 : :
1792 [ + + ]: 4478 : if (parent_rows > 0)
1793 : 4458 : reltarget->width = rint(parent_size / parent_rows);
1794 : :
1795 : 4478 : return reltarget;
1796 : : }
|