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