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