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