PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
prepunion.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/tlist.h"
#include "parser/parse_coerce.h"
#include "utils/selfuncs.h"
Include dependency graph for prepunion.c:

Go to the source code of this file.

Functions

static RelOptInforecurse_set_operations (Node *setOp, PlannerInfo *root, SetOperationStmt *parentOp, List *colTypes, List *colCollations, List *refnames_tlist, List **pTargetList, bool *istrivial_tlist)
 
static RelOptInfogenerate_recursion_path (SetOperationStmt *setOp, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
 
static void build_setop_child_paths (PlannerInfo *root, RelOptInfo *rel, bool trivial_tlist, List *child_tlist, List *interesting_pathkeys, double *pNumGroups)
 
static RelOptInfogenerate_union_paths (SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
 
static RelOptInfogenerate_nonunion_paths (SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
 
static Listplan_union_children (PlannerInfo *root, SetOperationStmt *top_union, List *refnames_tlist, List **tlist_list, List **istrivial_tlist)
 
static void postprocess_setop_rel (PlannerInfo *root, RelOptInfo *rel)
 
static Listgenerate_setop_tlist (List *colTypes, List *colCollations, Index varno, bool hack_constants, List *input_tlist, List *refnames_tlist, bool *trivial_tlist)
 
static Listgenerate_append_tlist (List *colTypes, List *colCollations, List *input_tlists, List *refnames_tlist)
 
static Listgenerate_setop_grouplist (SetOperationStmt *op, List *targetlist)
 
RelOptInfoplan_set_operations (PlannerInfo *root)
 

Function Documentation

◆ build_setop_child_paths()

static void build_setop_child_paths ( PlannerInfo root,
RelOptInfo rel,
bool  trivial_tlist,
List child_tlist,
List interesting_pathkeys,
double *  pNumGroups 
)
static

Definition at line 481 of file prepunion.c.

484{
485 RelOptInfo *final_rel;
486 List *setop_pathkeys = rel->subroot->setop_pathkeys;
487 ListCell *lc;
488
489 /* it can't be a set op child rel if it's not a subquery */
490 Assert(rel->rtekind == RTE_SUBQUERY);
491
492 /* when sorting is needed, add child rel equivalences */
493 if (interesting_pathkeys != NIL)
495 rel,
496 child_tlist,
497 interesting_pathkeys);
498
499 /*
500 * Mark rel with estimated output rows, width, etc. Note that we have to
501 * do this before generating outer-query paths, else cost_subqueryscan is
502 * not happy.
503 */
505
506 /*
507 * Since we may want to add a partial path to this relation, we must set
508 * its consider_parallel flag correctly.
509 */
510 final_rel = fetch_upper_rel(rel->subroot, UPPERREL_FINAL, NULL);
511 rel->consider_parallel = final_rel->consider_parallel;
512
513 /* Generate subquery scan paths for any interesting path in final_rel */
514 foreach(lc, final_rel->pathlist)
515 {
516 Path *subpath = (Path *) lfirst(lc);
517 List *pathkeys;
518 Path *cheapest_input_path = final_rel->cheapest_total_path;
519 bool is_sorted;
520 int presorted_keys;
521
522 /*
523 * Include the cheapest path as-is so that the set operation can be
524 * cheaply implemented using a method which does not require the input
525 * to be sorted.
526 */
527 if (subpath == cheapest_input_path)
528 {
529 /* Convert subpath's pathkeys to outer representation */
530 pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
532
533 /* Generate outer path using this subpath */
535 rel,
536 subpath,
537 trivial_tlist,
538 pathkeys,
539 NULL));
540 }
541
542 /* skip dealing with sorted paths if the setop doesn't need them */
543 if (interesting_pathkeys == NIL)
544 continue;
545
546 /*
547 * Create paths to suit final sort order required for setop_pathkeys.
548 * Here we'll sort the cheapest input path (if not sorted already) and
549 * incremental sort any paths which are partially sorted.
550 */
551 is_sorted = pathkeys_count_contained_in(setop_pathkeys,
552 subpath->pathkeys,
553 &presorted_keys);
554
555 if (!is_sorted)
556 {
557 double limittuples = rel->subroot->limit_tuples;
558
559 /*
560 * Try at least sorting the cheapest path and also try
561 * incrementally sorting any path which is partially sorted
562 * already (no need to deal with paths which have presorted keys
563 * when incremental sort is disabled unless it's the cheapest
564 * input path).
565 */
566 if (subpath != cheapest_input_path &&
567 (presorted_keys == 0 || !enable_incremental_sort))
568 continue;
569
570 /*
571 * We've no need to consider both a sort and incremental sort.
572 * We'll just do a sort if there are no presorted keys and an
573 * incremental sort when there are presorted keys.
574 */
575 if (presorted_keys == 0 || !enable_incremental_sort)
577 final_rel,
578 subpath,
579 setop_pathkeys,
580 limittuples);
581 else
583 final_rel,
584 subpath,
585 setop_pathkeys,
586 presorted_keys,
587 limittuples);
588 }
589
590 /*
591 * subpath is now sorted, so add it to the pathlist. We already added
592 * the cheapest_input_path above, so don't add it again unless we just
593 * sorted it.
594 */
595 if (subpath != cheapest_input_path)
596 {
597 /* Convert subpath's pathkeys to outer representation */
598 pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
600
601 /* Generate outer path using this subpath */
603 rel,
604 subpath,
605 trivial_tlist,
606 pathkeys,
607 NULL));
608 }
609 }
610
611 /* if consider_parallel is false, there should be no partial paths */
612 Assert(final_rel->consider_parallel ||
613 final_rel->partial_pathlist == NIL);
614
615 /*
616 * If we have a partial path for the child relation, we can use that to
617 * build a partial path for this relation. But there's no point in
618 * considering any path but the cheapest.
619 */
621 final_rel->partial_pathlist != NIL)
622 {
623 Path *partial_subpath;
624 Path *partial_path;
625
626 partial_subpath = linitial(final_rel->partial_pathlist);
627 partial_path = (Path *)
628 create_subqueryscan_path(root, rel, partial_subpath,
629 trivial_tlist,
630 NIL, NULL);
631 add_partial_path(rel, partial_path);
632 }
633
635
636 /*
637 * Estimate number of groups if caller wants it. If the subquery used
638 * grouping or aggregation, its output is probably mostly unique anyway;
639 * otherwise do statistical estimation.
640 *
641 * XXX you don't really want to know about this: we do the estimation
642 * using the subroot->parse's original targetlist expressions, not the
643 * subroot->processed_tlist which might seem more appropriate. The reason
644 * is that if the subquery is itself a setop, it may return a
645 * processed_tlist containing "varno 0" Vars generated by
646 * generate_append_tlist, and those would confuse estimate_num_groups
647 * mightily. We ought to get rid of the "varno 0" hack, but that requires
648 * a redesign of the parsetree representation of setops, so that there can
649 * be an RTE corresponding to each setop's output. Note, we use this not
650 * subquery's targetlist but subroot->parse's targetlist, because it was
651 * revised by self-join removal. subquery's targetlist might contain the
652 * references to the removed relids.
653 */
654 if (pNumGroups)
655 {
656 PlannerInfo *subroot = rel->subroot;
657 Query *subquery = subroot->parse;
658
659 if (subquery->groupClause || subquery->groupingSets ||
660 subquery->distinctClause || subroot->hasHavingQual ||
661 subquery->hasAggs)
662 *pNumGroups = rel->cheapest_total_path->rows;
663 else
664 *pNumGroups = estimate_num_groups(subroot,
665 get_tlist_exprs(subroot->parse->targetList, false),
667 NULL,
668 NULL);
669 }
670}
#define bms_is_empty(a)
Definition: bitmapset.h:118
void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition: costsize.c:5888
bool enable_incremental_sort
Definition: costsize.c:151
void add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel, List *child_tlist, List *setop_pathkeys)
Definition: equivclass.c:3084
Assert(PointerIsAligned(start, uint64))
Datum subpath(PG_FUNCTION_ARGS)
Definition: ltree_op.c:311
@ RTE_SUBQUERY
Definition: parsenodes.h:1027
bool pathkeys_count_contained_in(List *keys1, List *keys2, int *n_common)
Definition: pathkeys.c:558
List * convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, List *subquery_pathkeys, List *subquery_tlist)
Definition: pathkeys.c:1054
void add_partial_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:795
SubqueryScanPath * create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, bool trivial_pathtarget, List *pathkeys, Relids required_outer)
Definition: pathnode.c:2088
IncrementalSortPath * create_incremental_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, int presorted_keys, double limit_tuples)
Definition: pathnode.c:3032
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:3082
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:461
@ UPPERREL_FINAL
Definition: pathnodes.h:79
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
#define linitial(l)
Definition: pg_list.h:178
static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
Definition: prepunion.c:1331
tree ctl root
Definition: radixtree.h:1857
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1458
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset, EstimationInfo *estinfo)
Definition: selfuncs.c:3446
Definition: pg_list.h:54
Cardinality rows
Definition: pathnodes.h:1796
bool hasHavingQual
Definition: pathnodes.h:526
Query * parse
Definition: pathnodes.h:226
Cardinality limit_tuples
Definition: pathnodes.h:513
List * setop_pathkeys
Definition: pathnodes.h:428
List * groupClause
Definition: parsenodes.h:211
List * targetList
Definition: parsenodes.h:193
List * groupingSets
Definition: parsenodes.h:214
List * distinctClause
Definition: parsenodes.h:220
bool consider_parallel
Definition: pathnodes.h:914
Relids lateral_relids
Definition: pathnodes.h:940
List * pathlist
Definition: pathnodes.h:925
struct Path * cheapest_total_path
Definition: pathnodes.h:929
List * partial_pathlist
Definition: pathnodes.h:927
PlannerInfo * subroot
Definition: pathnodes.h:980
RTEKind rtekind
Definition: pathnodes.h:949
List * make_tlist_from_pathtarget(PathTarget *target)
Definition: tlist.c:624
List * get_tlist_exprs(List *tlist, bool includeJunk)
Definition: tlist.c:163

References add_partial_path(), add_path(), add_setop_child_rel_equivalences(), Assert(), bms_is_empty, RelOptInfo::cheapest_total_path, RelOptInfo::consider_parallel, convert_subquery_pathkeys(), create_incremental_sort_path(), create_sort_path(), create_subqueryscan_path(), Query::distinctClause, enable_incremental_sort, estimate_num_groups(), fetch_upper_rel(), get_tlist_exprs(), Query::groupClause, Query::groupingSets, PlannerInfo::hasHavingQual, RelOptInfo::lateral_relids, lfirst, PlannerInfo::limit_tuples, linitial, make_tlist_from_pathtarget(), NIL, PlannerInfo::parse, RelOptInfo::partial_pathlist, pathkeys_count_contained_in(), RelOptInfo::pathlist, postprocess_setop_rel(), root, Path::rows, RTE_SUBQUERY, RelOptInfo::rtekind, set_subquery_size_estimates(), PlannerInfo::setop_pathkeys, subpath(), RelOptInfo::subroot, Query::targetList, and UPPERREL_FINAL.

Referenced by generate_nonunion_paths(), generate_recursion_path(), and generate_union_paths().

◆ generate_append_tlist()

static List * generate_append_tlist ( List colTypes,
List colCollations,
List input_tlists,
List refnames_tlist 
)
static

Definition at line 1485 of file prepunion.c.

1488{
1489 List *tlist = NIL;
1490 int resno = 1;
1491 ListCell *curColType;
1492 ListCell *curColCollation;
1493 ListCell *ref_tl_item;
1494 int colindex;
1495 TargetEntry *tle;
1496 Node *expr;
1497 ListCell *tlistl;
1498 int32 *colTypmods;
1499
1500 /*
1501 * First extract typmods to use.
1502 *
1503 * If the inputs all agree on type and typmod of a particular column, use
1504 * that typmod; else use -1.
1505 */
1506 colTypmods = (int32 *) palloc(list_length(colTypes) * sizeof(int32));
1507
1508 foreach(tlistl, input_tlists)
1509 {
1510 List *subtlist = (List *) lfirst(tlistl);
1511 ListCell *subtlistl;
1512
1513 curColType = list_head(colTypes);
1514 colindex = 0;
1515 foreach(subtlistl, subtlist)
1516 {
1517 TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
1518
1519 Assert(!subtle->resjunk);
1520 Assert(curColType != NULL);
1521 if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1522 {
1523 /* If first subplan, copy the typmod; else compare */
1524 int32 subtypmod = exprTypmod((Node *) subtle->expr);
1525
1526 if (tlistl == list_head(input_tlists))
1527 colTypmods[colindex] = subtypmod;
1528 else if (subtypmod != colTypmods[colindex])
1529 colTypmods[colindex] = -1;
1530 }
1531 else
1532 {
1533 /* types disagree, so force typmod to -1 */
1534 colTypmods[colindex] = -1;
1535 }
1536 curColType = lnext(colTypes, curColType);
1537 colindex++;
1538 }
1539 Assert(curColType == NULL);
1540 }
1541
1542 /*
1543 * Now we can build the tlist for the Append.
1544 */
1545 colindex = 0;
1546 forthree(curColType, colTypes, curColCollation, colCollations,
1547 ref_tl_item, refnames_tlist)
1548 {
1549 Oid colType = lfirst_oid(curColType);
1550 int32 colTypmod = colTypmods[colindex++];
1551 Oid colColl = lfirst_oid(curColCollation);
1552 TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
1553
1554 Assert(reftle->resno == resno);
1555 Assert(!reftle->resjunk);
1556 expr = (Node *) makeVar(0,
1557 resno,
1558 colType,
1559 colTypmod,
1560 colColl,
1561 0);
1562 tle = makeTargetEntry((Expr *) expr,
1563 (AttrNumber) resno++,
1564 pstrdup(reftle->resname),
1565 false);
1566
1567 /*
1568 * By convention, all output columns in a setop tree have
1569 * ressortgroupref equal to their resno. In some cases the ref isn't
1570 * needed, but this is a cleaner way than modifying the tlist later.
1571 */
1572 tle->ressortgroupref = tle->resno;
1573
1574 tlist = lappend(tlist, tle);
1575 }
1576
1577 pfree(colTypmods);
1578
1579 return tlist;
1580}
int16 AttrNumber
Definition: attnum.h:21
int32_t int32
Definition: c.h:498
List * lappend(List *list, void *datum)
Definition: list.c:339
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:2321
void pfree(void *pointer)
Definition: mcxt.c:2146
void * palloc(Size size)
Definition: mcxt.c:1939
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
int32 exprTypmod(const Node *expr)
Definition: nodeFuncs.c:301
static int list_length(const List *l)
Definition: pg_list.h:152
#define forthree(cell1, list1, cell2, list2, cell3, list3)
Definition: pg_list.h:563
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:343
#define lfirst_oid(lc)
Definition: pg_list.h:174
unsigned int Oid
Definition: postgres_ext.h:30
Definition: nodes.h:135
Expr * expr
Definition: primnodes.h:2219
AttrNumber resno
Definition: primnodes.h:2221
Index ressortgroupref
Definition: primnodes.h:2225

References Assert(), TargetEntry::expr, exprType(), exprTypmod(), forthree, lappend(), lfirst, lfirst_oid, list_head(), list_length(), lnext(), makeTargetEntry(), makeVar(), NIL, palloc(), pfree(), pstrdup(), TargetEntry::resno, and TargetEntry::ressortgroupref.

Referenced by generate_recursion_path(), and generate_union_paths().

◆ generate_nonunion_paths()

static RelOptInfo * generate_nonunion_paths ( SetOperationStmt op,
PlannerInfo root,
List refnames_tlist,
List **  pTargetList 
)
static

Definition at line 998 of file prepunion.c.

1001{
1002 RelOptInfo *result_rel;
1003 RelOptInfo *lrel,
1004 *rrel;
1005 double save_fraction = root->tuple_fraction;
1006 Path *lpath,
1007 *rpath,
1008 *path;
1009 List *lpath_tlist,
1010 *rpath_tlist,
1011 *tlist,
1012 *groupList;
1013 bool lpath_trivial_tlist,
1014 rpath_trivial_tlist,
1015 result_trivial_tlist;
1016 List *nonunion_pathkeys = NIL;
1017 double dLeftGroups,
1018 dRightGroups,
1019 dNumGroups,
1020 dNumOutputRows;
1021 bool can_sort;
1022 bool can_hash;
1023 SetOpCmd cmd;
1024
1025 /*
1026 * Tell children to fetch all tuples.
1027 */
1028 root->tuple_fraction = 0.0;
1029
1030 /* Recurse on children */
1031 lrel = recurse_set_operations(op->larg, root,
1032 op,
1033 op->colTypes, op->colCollations,
1034 refnames_tlist,
1035 &lpath_tlist,
1036 &lpath_trivial_tlist);
1037
1038 rrel = recurse_set_operations(op->rarg, root,
1039 op,
1040 op->colTypes, op->colCollations,
1041 refnames_tlist,
1042 &rpath_tlist,
1043 &rpath_trivial_tlist);
1044
1045 /*
1046 * Generate tlist for SetOp plan node.
1047 *
1048 * The tlist for a SetOp plan isn't important so far as the SetOp is
1049 * concerned, but we must make it look real anyway for the benefit of the
1050 * next plan level up.
1051 */
1052 tlist = generate_setop_tlist(op->colTypes, op->colCollations,
1053 0, false, lpath_tlist, refnames_tlist,
1054 &result_trivial_tlist);
1055
1056 /* We should not have needed any type coercions in the tlist */
1057 Assert(result_trivial_tlist);
1058
1059 *pTargetList = tlist;
1060
1061 /* Identify the grouping semantics */
1062 groupList = generate_setop_grouplist(op, tlist);
1063
1064 /* Check whether the operators support sorting or hashing */
1065 can_sort = grouping_is_sortable(groupList);
1066 can_hash = grouping_is_hashable(groupList);
1067 if (!can_sort && !can_hash)
1068 ereport(ERROR,
1069 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1070 /* translator: %s is INTERSECT or EXCEPT */
1071 errmsg("could not implement %s",
1072 (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT"),
1073 errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
1074
1075 if (can_sort)
1076 {
1077 /* Determine the pathkeys for sorting by the whole target list */
1078 nonunion_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
1079 tlist);
1080
1081 root->query_pathkeys = nonunion_pathkeys;
1082 }
1083
1084 /*
1085 * Now that we've got all that info, we can build the child paths.
1086 */
1087 if (lrel->rtekind == RTE_SUBQUERY)
1088 build_setop_child_paths(root, lrel, lpath_trivial_tlist, lpath_tlist,
1089 nonunion_pathkeys, &dLeftGroups);
1090 else
1091 dLeftGroups = lrel->rows;
1092 if (rrel->rtekind == RTE_SUBQUERY)
1093 build_setop_child_paths(root, rrel, rpath_trivial_tlist, rpath_tlist,
1094 nonunion_pathkeys, &dRightGroups);
1095 else
1096 dRightGroups = rrel->rows;
1097
1098 /* Undo effects of forcing tuple_fraction to 0 */
1099 root->tuple_fraction = save_fraction;
1100
1101 /*
1102 * For EXCEPT, we must put the left input first. For INTERSECT, either
1103 * order should give the same results, and we prefer to put the smaller
1104 * input first in order to (a) minimize the size of the hash table in the
1105 * hashing case, and (b) improve our chances of exploiting the executor's
1106 * fast path for empty left-hand input. "Smaller" means the one with the
1107 * fewer groups.
1108 */
1109 if (op->op != SETOP_EXCEPT && dLeftGroups > dRightGroups)
1110 {
1111 /* need to swap the two inputs */
1112 RelOptInfo *tmprel;
1113 List *tmplist;
1114 double tmpd;
1115
1116 tmprel = lrel;
1117 lrel = rrel;
1118 rrel = tmprel;
1119 tmplist = lpath_tlist;
1120 lpath_tlist = rpath_tlist;
1121 rpath_tlist = tmplist;
1122 tmpd = dLeftGroups;
1123 dLeftGroups = dRightGroups;
1124 dRightGroups = tmpd;
1125 }
1126
1127 lpath = lrel->cheapest_total_path;
1128 rpath = rrel->cheapest_total_path;
1129
1130 /* Build result relation. */
1131 result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
1132 bms_union(lrel->relids, rrel->relids));
1133 result_rel->reltarget = create_pathtarget(root, tlist);
1134
1135 /*
1136 * Estimate number of distinct groups that we'll need hashtable entries
1137 * for; this is the size of the left-hand input for EXCEPT, or the smaller
1138 * input for INTERSECT. Also estimate the number of eventual output rows.
1139 * In non-ALL cases, we estimate each group produces one output row; in
1140 * ALL cases use the relevant relation size. These are worst-case
1141 * estimates, of course, but we need to be conservative.
1142 */
1143 if (op->op == SETOP_EXCEPT)
1144 {
1145 dNumGroups = dLeftGroups;
1146 dNumOutputRows = op->all ? lpath->rows : dNumGroups;
1147 }
1148 else
1149 {
1150 dNumGroups = dLeftGroups;
1151 dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
1152 }
1153 result_rel->rows = dNumOutputRows;
1154
1155 /* Select the SetOpCmd type */
1156 switch (op->op)
1157 {
1158 case SETOP_INTERSECT:
1160 break;
1161 case SETOP_EXCEPT:
1163 break;
1164 default:
1165 elog(ERROR, "unrecognized set op: %d", (int) op->op);
1166 cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */
1167 break;
1168 }
1169
1170 /*
1171 * If we can hash, that just requires a SetOp atop the cheapest inputs.
1172 */
1173 if (can_hash)
1174 {
1175 path = (Path *) create_setop_path(root,
1176 result_rel,
1177 lpath,
1178 rpath,
1179 cmd,
1181 groupList,
1182 dNumGroups,
1183 dNumOutputRows);
1184 add_path(result_rel, path);
1185 }
1186
1187 /*
1188 * If we can sort, generate the cheapest sorted input paths, and add a
1189 * SetOp atop those.
1190 */
1191 if (can_sort)
1192 {
1193 List *pathkeys;
1194 Path *slpath,
1195 *srpath;
1196
1197 /* First the left input ... */
1199 groupList,
1200 lpath_tlist);
1201 if (pathkeys_contained_in(pathkeys, lpath->pathkeys))
1202 slpath = lpath; /* cheapest path is already sorted */
1203 else
1204 {
1206 nonunion_pathkeys,
1207 NULL,
1208 TOTAL_COST,
1209 false);
1210 /* Subquery failed to produce any presorted paths? */
1211 if (slpath == NULL)
1212 slpath = (Path *) create_sort_path(root,
1213 lpath->parent,
1214 lpath,
1215 pathkeys,
1216 -1.0);
1217 }
1218
1219 /* and now the same for the right. */
1221 groupList,
1222 rpath_tlist);
1223 if (pathkeys_contained_in(pathkeys, rpath->pathkeys))
1224 srpath = rpath; /* cheapest path is already sorted */
1225 else
1226 {
1228 nonunion_pathkeys,
1229 NULL,
1230 TOTAL_COST,
1231 false);
1232 /* Subquery failed to produce any presorted paths? */
1233 if (srpath == NULL)
1234 srpath = (Path *) create_sort_path(root,
1235 rpath->parent,
1236 rpath,
1237 pathkeys,
1238 -1.0);
1239 }
1240
1241 path = (Path *) create_setop_path(root,
1242 result_rel,
1243 slpath,
1244 srpath,
1245 cmd,
1247 groupList,
1248 dNumGroups,
1249 dNumOutputRows);
1250 add_path(result_rel, path);
1251 }
1252
1253 return result_rel;
1254}
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:251
#define Min(x, y)
Definition: c.h:975
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:149
SetOpCmd
Definition: nodes.h:403
@ SETOPCMD_EXCEPT
Definition: nodes.h:406
@ SETOPCMD_EXCEPT_ALL
Definition: nodes.h:407
@ SETOPCMD_INTERSECT_ALL
Definition: nodes.h:405
@ SETOPCMD_INTERSECT
Definition: nodes.h:404
@ SETOP_HASHED
Definition: nodes.h:413
@ SETOP_SORTED
Definition: nodes.h:412
@ SETOP_INTERSECT
Definition: parsenodes.h:2169
@ SETOP_EXCEPT
Definition: parsenodes.h:2170
Path * get_cheapest_path_for_pathkeys(List *paths, List *pathkeys, Relids required_outer, CostSelector cost_criterion, bool require_parallel_safe)
Definition: pathkeys.c:620
List * make_pathkeys_for_sortclauses(PlannerInfo *root, List *sortclauses, List *tlist)
Definition: pathkeys.c:1336
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:3650
@ TOTAL_COST
Definition: pathnodes.h:38
@ UPPERREL_SETOP
Definition: pathnodes.h:71
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1594
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:1357
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:209
static void build_setop_child_paths(PlannerInfo *root, RelOptInfo *rel, bool trivial_tlist, List *child_tlist, List *interesting_pathkeys, double *pNumGroups)
Definition: prepunion.c:481
List * pathkeys
Definition: pathnodes.h:1802
Relids relids
Definition: pathnodes.h:898
struct PathTarget * reltarget
Definition: pathnodes.h:920
Cardinality rows
Definition: pathnodes.h:904
SetOperation op
Definition: parsenodes.h:2247
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:540
bool grouping_is_hashable(List *groupClause)
Definition: tlist.c:560
#define create_pathtarget(root, tlist)
Definition: tlist.h:53

References add_path(), SetOperationStmt::all, Assert(), bms_union(), build_setop_child_paths(), RelOptInfo::cheapest_total_path, create_pathtarget, create_setop_path(), create_sort_path(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, fetch_upper_rel(), generate_setop_grouplist(), generate_setop_tlist(), get_cheapest_path_for_pathkeys(), grouping_is_hashable(), grouping_is_sortable(), SetOperationStmt::larg, make_pathkeys_for_sortclauses(), Min, NIL, SetOperationStmt::op, Path::pathkeys, pathkeys_contained_in(), RelOptInfo::pathlist, SetOperationStmt::rarg, recurse_set_operations(), RelOptInfo::relids, RelOptInfo::reltarget, root, RelOptInfo::rows, Path::rows, RTE_SUBQUERY, RelOptInfo::rtekind, SETOP_EXCEPT, SETOP_HASHED, SETOP_INTERSECT, SETOP_SORTED, SETOPCMD_EXCEPT, SETOPCMD_EXCEPT_ALL, SETOPCMD_INTERSECT, SETOPCMD_INTERSECT_ALL, TOTAL_COST, and UPPERREL_SETOP.

Referenced by recurse_set_operations().

◆ generate_recursion_path()

static RelOptInfo * generate_recursion_path ( SetOperationStmt setOp,
PlannerInfo root,
List refnames_tlist,
List **  pTargetList 
)
static

Definition at line 357 of file prepunion.c.

360{
361 RelOptInfo *result_rel;
362 Path *path;
363 RelOptInfo *lrel,
364 *rrel;
365 Path *lpath;
366 Path *rpath;
367 List *lpath_tlist;
368 bool lpath_trivial_tlist;
369 List *rpath_tlist;
370 bool rpath_trivial_tlist;
371 List *tlist;
372 List *groupList;
373 double dNumGroups;
374
375 /* Parser should have rejected other cases */
376 if (setOp->op != SETOP_UNION)
377 elog(ERROR, "only UNION queries can be recursive");
378 /* Worktable ID should be assigned */
379 Assert(root->wt_param_id >= 0);
380
381 /*
382 * Unlike a regular UNION node, process the left and right inputs
383 * separately without any intention of combining them into one Append.
384 */
385 lrel = recurse_set_operations(setOp->larg, root,
386 NULL, /* no value in sorted results */
387 setOp->colTypes, setOp->colCollations,
388 refnames_tlist,
389 &lpath_tlist,
390 &lpath_trivial_tlist);
391 if (lrel->rtekind == RTE_SUBQUERY)
392 build_setop_child_paths(root, lrel, lpath_trivial_tlist, lpath_tlist,
393 NIL, NULL);
394 lpath = lrel->cheapest_total_path;
395 /* The right path will want to look at the left one ... */
396 root->non_recursive_path = lpath;
397 rrel = recurse_set_operations(setOp->rarg, root,
398 NULL, /* no value in sorted results */
399 setOp->colTypes, setOp->colCollations,
400 refnames_tlist,
401 &rpath_tlist,
402 &rpath_trivial_tlist);
403 if (rrel->rtekind == RTE_SUBQUERY)
404 build_setop_child_paths(root, rrel, rpath_trivial_tlist, rpath_tlist,
405 NIL, NULL);
406 rpath = rrel->cheapest_total_path;
407 root->non_recursive_path = NULL;
408
409 /*
410 * Generate tlist for RecursiveUnion path node --- same as in Append cases
411 */
412 tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations,
413 list_make2(lpath_tlist, rpath_tlist),
414 refnames_tlist);
415
416 *pTargetList = tlist;
417
418 /* Build result relation. */
419 result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
420 bms_union(lrel->relids, rrel->relids));
421 result_rel->reltarget = create_pathtarget(root, tlist);
422
423 /*
424 * If UNION, identify the grouping operators
425 */
426 if (setOp->all)
427 {
428 groupList = NIL;
429 dNumGroups = 0;
430 }
431 else
432 {
433 /* Identify the grouping semantics */
434 groupList = generate_setop_grouplist(setOp, tlist);
435
436 /* We only support hashing here */
437 if (!grouping_is_hashable(groupList))
439 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
440 errmsg("could not implement recursive UNION"),
441 errdetail("All column datatypes must be hashable.")));
442
443 /*
444 * For the moment, take the number of distinct groups as equal to the
445 * total input size, ie, the worst case.
446 */
447 dNumGroups = lpath->rows + rpath->rows * 10;
448 }
449
450 /*
451 * And make the path node.
452 */
454 result_rel,
455 lpath,
456 rpath,
457 result_rel->reltarget,
458 groupList,
459 root->wt_param_id,
460 dNumGroups);
461
462 add_path(result_rel, path);
463 postprocess_setop_rel(root, result_rel);
464 return result_rel;
465}
@ SETOP_UNION
Definition: parsenodes.h:2168
RecursiveUnionPath * create_recursiveunion_path(PlannerInfo *root, RelOptInfo *rel, Path *leftpath, Path *rightpath, PathTarget *target, List *distinctList, int wtParam, double numGroups)
Definition: pathnode.c:3768
#define list_make2(x1, x2)
Definition: pg_list.h:214
static List * generate_append_tlist(List *colTypes, List *colCollations, List *input_tlists, List *refnames_tlist)
Definition: prepunion.c:1485

References add_path(), SetOperationStmt::all, Assert(), bms_union(), build_setop_child_paths(), RelOptInfo::cheapest_total_path, create_pathtarget, create_recursiveunion_path(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, fetch_upper_rel(), generate_append_tlist(), generate_setop_grouplist(), grouping_is_hashable(), SetOperationStmt::larg, list_make2, NIL, SetOperationStmt::op, postprocess_setop_rel(), SetOperationStmt::rarg, recurse_set_operations(), RelOptInfo::relids, RelOptInfo::reltarget, root, Path::rows, RTE_SUBQUERY, RelOptInfo::rtekind, SETOP_UNION, and UPPERREL_SETOP.

Referenced by plan_set_operations().

◆ generate_setop_grouplist()

static List * generate_setop_grouplist ( SetOperationStmt op,
List targetlist 
)
static

Definition at line 1594 of file prepunion.c.

1595{
1596 List *grouplist = copyObject(op->groupClauses);
1597 ListCell *lg;
1598 ListCell *lt;
1599
1600 lg = list_head(grouplist);
1601 foreach(lt, targetlist)
1602 {
1603 TargetEntry *tle = (TargetEntry *) lfirst(lt);
1604 SortGroupClause *sgc;
1605
1606 Assert(!tle->resjunk);
1607
1608 /* non-resjunk columns should have sortgroupref = resno */
1609 Assert(tle->ressortgroupref == tle->resno);
1610
1611 /* non-resjunk columns should have grouping clauses */
1612 Assert(lg != NULL);
1613 sgc = (SortGroupClause *) lfirst(lg);
1614 lg = lnext(grouplist, lg);
1615 Assert(sgc->tleSortGroupRef == 0);
1616
1617 sgc->tleSortGroupRef = tle->ressortgroupref;
1618 }
1619 Assert(lg == NULL);
1620 return grouplist;
1621}
#define copyObject(obj)
Definition: nodes.h:230
Index tleSortGroupRef
Definition: parsenodes.h:1452

References Assert(), copyObject, lfirst, list_head(), lnext(), TargetEntry::resno, TargetEntry::ressortgroupref, and SortGroupClause::tleSortGroupRef.

Referenced by generate_nonunion_paths(), generate_recursion_path(), and generate_union_paths().

◆ generate_setop_tlist()

static List * generate_setop_tlist ( List colTypes,
List colCollations,
Index  varno,
bool  hack_constants,
List input_tlist,
List refnames_tlist,
bool *  trivial_tlist 
)
static

Definition at line 1357 of file prepunion.c.

1363{
1364 List *tlist = NIL;
1365 int resno = 1;
1366 ListCell *ctlc,
1367 *cclc,
1368 *itlc,
1369 *rtlc;
1370 TargetEntry *tle;
1371 Node *expr;
1372
1373 *trivial_tlist = true; /* until proven differently */
1374
1375 forfour(ctlc, colTypes, cclc, colCollations,
1376 itlc, input_tlist, rtlc, refnames_tlist)
1377 {
1378 Oid colType = lfirst_oid(ctlc);
1379 Oid colColl = lfirst_oid(cclc);
1380 TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
1381 TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
1382
1383 Assert(inputtle->resno == resno);
1384 Assert(reftle->resno == resno);
1385 Assert(!inputtle->resjunk);
1386 Assert(!reftle->resjunk);
1387
1388 /*
1389 * Generate columns referencing input columns and having appropriate
1390 * data types and column names. Insert datatype coercions where
1391 * necessary.
1392 *
1393 * HACK: constants in the input's targetlist are copied up as-is
1394 * rather than being referenced as subquery outputs. This is mainly
1395 * to ensure that when we try to coerce them to the output column's
1396 * datatype, the right things happen for UNKNOWN constants. But do
1397 * this only at the first level of subquery-scan plans; we don't want
1398 * phony constants appearing in the output tlists of upper-level
1399 * nodes!
1400 *
1401 * Note that copying a constant doesn't in itself require us to mark
1402 * the tlist nontrivial; see trivial_subqueryscan() in setrefs.c.
1403 */
1404 if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
1405 expr = (Node *) inputtle->expr;
1406 else
1407 expr = (Node *) makeVar(varno,
1408 inputtle->resno,
1409 exprType((Node *) inputtle->expr),
1410 exprTypmod((Node *) inputtle->expr),
1411 exprCollation((Node *) inputtle->expr),
1412 0);
1413
1414 if (exprType(expr) != colType)
1415 {
1416 /*
1417 * Note: it's not really cool to be applying coerce_to_common_type
1418 * here; one notable point is that assign_expr_collations never
1419 * gets run on any generated nodes. For the moment that's not a
1420 * problem because we force the correct exposed collation below.
1421 * It would likely be best to make the parser generate the correct
1422 * output tlist for every set-op to begin with, though.
1423 */
1424 expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */
1425 expr,
1426 colType,
1427 "UNION/INTERSECT/EXCEPT");
1428 *trivial_tlist = false; /* the coercion makes it not trivial */
1429 }
1430
1431 /*
1432 * Ensure the tlist entry's exposed collation matches the set-op. This
1433 * is necessary because plan_set_operations() reports the result
1434 * ordering as a list of SortGroupClauses, which don't carry collation
1435 * themselves but just refer to tlist entries. If we don't show the
1436 * right collation then planner.c might do the wrong thing in
1437 * higher-level queries.
1438 *
1439 * Note we use RelabelType, not CollateExpr, since this expression
1440 * will reach the executor without any further processing.
1441 */
1442 if (exprCollation(expr) != colColl)
1443 {
1444 expr = applyRelabelType(expr,
1445 exprType(expr), exprTypmod(expr), colColl,
1446 COERCE_IMPLICIT_CAST, -1, false);
1447 *trivial_tlist = false; /* the relabel makes it not trivial */
1448 }
1449
1450 tle = makeTargetEntry((Expr *) expr,
1451 (AttrNumber) resno++,
1452 pstrdup(reftle->resname),
1453 false);
1454
1455 /*
1456 * By convention, all output columns in a setop tree have
1457 * ressortgroupref equal to their resno. In some cases the ref isn't
1458 * needed, but this is a cleaner way than modifying the tlist later.
1459 */
1460 tle->ressortgroupref = tle->resno;
1461
1462 tlist = lappend(tlist, tle);
1463 }
1464
1465 return tlist;
1466}
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:821
Node * applyRelabelType(Node *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat, int rlocation, bool overwrite_ok)
Definition: nodeFuncs.c:636
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
Node * coerce_to_common_type(ParseState *pstate, Node *node, Oid targetTypeId, const char *context)
#define forfour(cell1, list1, cell2, list2, cell3, list3, cell4, list4)
Definition: pg_list.h:575
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:753

References applyRelabelType(), Assert(), COERCE_IMPLICIT_CAST, coerce_to_common_type(), TargetEntry::expr, exprCollation(), exprType(), exprTypmod(), forfour, IsA, lappend(), lfirst, lfirst_oid, makeTargetEntry(), makeVar(), NIL, pstrdup(), TargetEntry::resno, and TargetEntry::ressortgroupref.

Referenced by generate_nonunion_paths(), and recurse_set_operations().

◆ generate_union_paths()

static RelOptInfo * generate_union_paths ( SetOperationStmt op,
PlannerInfo root,
List refnames_tlist,
List **  pTargetList 
)
static

Definition at line 676 of file prepunion.c.

679{
680 Relids relids = NULL;
681 RelOptInfo *result_rel;
682 ListCell *lc;
683 ListCell *lc2;
684 ListCell *lc3;
685 List *cheapest_pathlist = NIL;
686 List *ordered_pathlist = NIL;
687 List *partial_pathlist = NIL;
688 bool partial_paths_valid = true;
689 bool consider_parallel = true;
690 List *rellist;
691 List *tlist_list;
692 List *trivial_tlist_list;
693 List *tlist;
694 List *groupList = NIL;
695 Path *apath;
696 Path *gpath = NULL;
697 bool try_sorted = false;
698 List *union_pathkeys = NIL;
699
700 /*
701 * If any of my children are identical UNION nodes (same op, all-flag, and
702 * colTypes/colCollations) then they can be merged into this node so that
703 * we generate only one Append/MergeAppend and unique-ification for the
704 * lot. Recurse to find such nodes.
705 */
706 rellist = plan_union_children(root,
707 op,
708 refnames_tlist,
709 &tlist_list,
710 &trivial_tlist_list);
711
712 /*
713 * Generate tlist for Append/MergeAppend plan node.
714 *
715 * The tlist for an Append plan isn't important as far as the Append is
716 * concerned, but we must make it look real anyway for the benefit of the
717 * next plan level up.
718 */
719 tlist = generate_append_tlist(op->colTypes, op->colCollations,
720 tlist_list, refnames_tlist);
721 *pTargetList = tlist;
722
723 /* For UNIONs (not UNION ALL), try sorting, if sorting is possible */
724 if (!op->all)
725 {
726 /* Identify the grouping semantics */
727 groupList = generate_setop_grouplist(op, tlist);
728
729 if (grouping_is_sortable(op->groupClauses))
730 {
731 try_sorted = true;
732 /* Determine the pathkeys for sorting by the whole target list */
733 union_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
734 tlist);
735
736 root->query_pathkeys = union_pathkeys;
737 }
738 }
739
740 /*
741 * Now that we've got the append target list, we can build the union child
742 * paths.
743 */
744 forthree(lc, rellist, lc2, trivial_tlist_list, lc3, tlist_list)
745 {
746 RelOptInfo *rel = lfirst(lc);
747 bool trivial_tlist = lfirst_int(lc2);
748 List *child_tlist = lfirst_node(List, lc3);
749
750 /* only build paths for the union children */
751 if (rel->rtekind == RTE_SUBQUERY)
752 build_setop_child_paths(root, rel, trivial_tlist, child_tlist,
753 union_pathkeys, NULL);
754 }
755
756 /* Build path lists and relid set. */
757 foreach(lc, rellist)
758 {
759 RelOptInfo *rel = lfirst(lc);
760 Path *ordered_path;
761
762 cheapest_pathlist = lappend(cheapest_pathlist,
764
765 if (try_sorted)
766 {
767 ordered_path = get_cheapest_path_for_pathkeys(rel->pathlist,
768 union_pathkeys,
769 NULL,
771 false);
772
773 if (ordered_path != NULL)
774 ordered_pathlist = lappend(ordered_pathlist, ordered_path);
775 else
776 {
777 /*
778 * If we can't find a sorted path, just give up trying to
779 * generate a list of correctly sorted child paths. This can
780 * happen when type coercion was added to the targetlist due
781 * to mismatching types from the union children.
782 */
783 try_sorted = false;
784 }
785 }
786
787 if (consider_parallel)
788 {
789 if (!rel->consider_parallel)
790 {
791 consider_parallel = false;
792 partial_paths_valid = false;
793 }
794 else if (rel->partial_pathlist == NIL)
795 partial_paths_valid = false;
796 else
797 partial_pathlist = lappend(partial_pathlist,
799 }
800
801 relids = bms_union(relids, rel->relids);
802 }
803
804 /* Build result relation. */
805 result_rel = fetch_upper_rel(root, UPPERREL_SETOP, relids);
806 result_rel->reltarget = create_pathtarget(root, tlist);
807 result_rel->consider_parallel = consider_parallel;
808 result_rel->consider_startup = (root->tuple_fraction > 0);
809
810 /*
811 * Append the child results together using the cheapest paths from each
812 * union child.
813 */
814 apath = (Path *) create_append_path(root, result_rel, cheapest_pathlist,
815 NIL, NIL, NULL, 0, false, -1);
816
817 /*
818 * Estimate number of groups. For now we just assume the output is unique
819 * --- this is certainly true for the UNION case, and we want worst-case
820 * estimates anyway.
821 */
822 result_rel->rows = apath->rows;
823
824 /*
825 * Now consider doing the same thing using the partial paths plus Append
826 * plus Gather.
827 */
828 if (partial_paths_valid)
829 {
830 Path *papath;
831 int parallel_workers = 0;
832
833 /* Find the highest number of workers requested for any subpath. */
834 foreach(lc, partial_pathlist)
835 {
836 Path *subpath = lfirst(lc);
837
838 parallel_workers = Max(parallel_workers,
839 subpath->parallel_workers);
840 }
841 Assert(parallel_workers > 0);
842
843 /*
844 * If the use of parallel append is permitted, always request at least
845 * log2(# of children) paths. We assume it can be useful to have
846 * extra workers in this case because they will be spread out across
847 * the children. The precise formula is just a guess; see
848 * add_paths_to_append_rel.
849 */
851 {
852 parallel_workers = Max(parallel_workers,
853 pg_leftmost_one_pos32(list_length(partial_pathlist)) + 1);
854 parallel_workers = Min(parallel_workers,
856 }
857 Assert(parallel_workers > 0);
858
859 papath = (Path *)
860 create_append_path(root, result_rel, NIL, partial_pathlist,
861 NIL, NULL, parallel_workers,
863 gpath = (Path *)
864 create_gather_path(root, result_rel, papath,
865 result_rel->reltarget, NULL, NULL);
866 }
867
868 if (!op->all)
869 {
870 double dNumGroups;
871 bool can_sort = grouping_is_sortable(groupList);
872 bool can_hash = grouping_is_hashable(groupList);
873
874 /*
875 * XXX for the moment, take the number of distinct groups as equal to
876 * the total input size, i.e., the worst case. This is too
877 * conservative, but it's not clear how to get a decent estimate of
878 * the true size. One should note as well the propensity of novices
879 * to write UNION rather than UNION ALL even when they don't expect
880 * any duplicates...
881 */
882 dNumGroups = apath->rows;
883
884 if (can_hash)
885 {
886 Path *path;
887
888 /*
889 * Try a hash aggregate plan on 'apath'. This is the cheapest
890 * available path containing each append child.
891 */
892 path = (Path *) create_agg_path(root,
893 result_rel,
894 apath,
895 create_pathtarget(root, tlist),
898 groupList,
899 NIL,
900 NULL,
901 dNumGroups);
902 add_path(result_rel, path);
903
904 /* Try hash aggregate on the Gather path, if valid */
905 if (gpath != NULL)
906 {
907 /* Hashed aggregate plan --- no sort needed */
908 path = (Path *) create_agg_path(root,
909 result_rel,
910 gpath,
911 create_pathtarget(root, tlist),
914 groupList,
915 NIL,
916 NULL,
917 dNumGroups);
918 add_path(result_rel, path);
919 }
920 }
921
922 if (can_sort)
923 {
924 Path *path = apath;
925
926 /* Try Sort -> Unique on the Append path */
927 if (groupList != NIL)
928 path = (Path *) create_sort_path(root, result_rel, path,
929 make_pathkeys_for_sortclauses(root, groupList, tlist),
930 -1.0);
931
933 result_rel,
934 path,
935 list_length(path->pathkeys),
936 dNumGroups);
937
938 add_path(result_rel, path);
939
940 /* Try Sort -> Unique on the Gather path, if set */
941 if (gpath != NULL)
942 {
943 path = gpath;
944
945 path = (Path *) create_sort_path(root, result_rel, path,
946 make_pathkeys_for_sortclauses(root, groupList, tlist),
947 -1.0);
948
950 result_rel,
951 path,
952 list_length(path->pathkeys),
953 dNumGroups);
954 add_path(result_rel, path);
955 }
956 }
957
958 /*
959 * Try making a MergeAppend path if we managed to find a path with the
960 * correct pathkeys in each union child query.
961 */
962 if (try_sorted && groupList != NIL)
963 {
964 Path *path;
965
967 result_rel,
968 ordered_pathlist,
969 union_pathkeys,
970 NULL);
971
972 /* and make the MergeAppend unique */
974 result_rel,
975 path,
976 list_length(tlist),
977 dNumGroups);
978
979 add_path(result_rel, path);
980 }
981 }
982 else
983 {
984 /* UNION ALL */
985 add_path(result_rel, apath);
986
987 if (gpath != NULL)
988 add_path(result_rel, gpath);
989 }
990
991 return result_rel;
992}
#define Max(x, y)
Definition: c.h:969
int max_parallel_workers_per_gather
Definition: costsize.c:143
bool enable_parallel_append
Definition: costsize.c:161
@ AGG_HASHED
Definition: nodes.h:362
@ AGGSPLIT_SIMPLE
Definition: nodes.h:383
AppendPath * create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *partial_subpaths, List *pathkeys, Relids required_outer, int parallel_workers, bool parallel_aware, double rows)
Definition: pathnode.c:1300
GatherPath * create_gather_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target, Relids required_outer, double *rows)
Definition: pathnode.c:2044
UpperUniquePath * create_upper_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, int numCols, double numGroups)
Definition: pathnode.c:3187
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:3240
MergeAppendPath * create_merge_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *pathkeys, Relids required_outer)
Definition: pathnode.c:1471
static int pg_leftmost_one_pos32(uint32 word)
Definition: pg_bitutils.h:41
#define lfirst_node(type, lc)
Definition: pg_list.h:176
#define lfirst_int(lc)
Definition: pg_list.h:173
static List * plan_union_children(PlannerInfo *root, SetOperationStmt *top_union, List *refnames_tlist, List **tlist_list, List **istrivial_tlist)
Definition: prepunion.c:1269
bool consider_startup
Definition: pathnodes.h:910

References add_path(), AGG_HASHED, AGGSPLIT_SIMPLE, SetOperationStmt::all, Assert(), bms_union(), build_setop_child_paths(), RelOptInfo::cheapest_total_path, RelOptInfo::consider_parallel, RelOptInfo::consider_startup, create_agg_path(), create_append_path(), create_gather_path(), create_merge_append_path(), create_pathtarget, create_sort_path(), create_upper_unique_path(), enable_parallel_append, fetch_upper_rel(), forthree, generate_append_tlist(), generate_setop_grouplist(), get_cheapest_path_for_pathkeys(), grouping_is_hashable(), grouping_is_sortable(), lappend(), lfirst, lfirst_int, lfirst_node, linitial, list_length(), make_pathkeys_for_sortclauses(), Max, max_parallel_workers_per_gather, Min, NIL, RelOptInfo::partial_pathlist, Path::pathkeys, RelOptInfo::pathlist, pg_leftmost_one_pos32(), plan_union_children(), RelOptInfo::relids, RelOptInfo::reltarget, root, RelOptInfo::rows, Path::rows, RTE_SUBQUERY, RelOptInfo::rtekind, subpath(), TOTAL_COST, and UPPERREL_SETOP.

Referenced by recurse_set_operations().

◆ plan_set_operations()

RelOptInfo * plan_set_operations ( PlannerInfo root)

Definition at line 93 of file prepunion.c.

94{
95 Query *parse = root->parse;
96 SetOperationStmt *topop = castNode(SetOperationStmt, parse->setOperations);
97 Node *node;
98 RangeTblEntry *leftmostRTE;
99 Query *leftmostQuery;
100 RelOptInfo *setop_rel;
101 List *top_tlist;
102
103 Assert(topop);
104
105 /* check for unsupported stuff */
106 Assert(parse->jointree->fromlist == NIL);
107 Assert(parse->jointree->quals == NULL);
108 Assert(parse->groupClause == NIL);
109 Assert(parse->havingQual == NULL);
110 Assert(parse->windowClause == NIL);
111 Assert(parse->distinctClause == NIL);
112
113 /*
114 * In the outer query level, equivalence classes are limited to classes
115 * which define that the top-level target entry is equivalent to the
116 * corresponding child target entry. There won't be any equivalence class
117 * merging. Mark that merging is complete to allow us to make pathkeys.
118 */
119 Assert(root->eq_classes == NIL);
120 root->ec_merging_done = true;
121
122 /*
123 * We'll need to build RelOptInfos for each of the leaf subqueries, which
124 * are RTE_SUBQUERY rangetable entries in this Query. Prepare the index
125 * arrays for those, and for AppendRelInfos in case they're needed.
126 */
128
129 /*
130 * Find the leftmost component Query. We need to use its column names for
131 * all generated tlists (else SELECT INTO won't work right).
132 */
133 node = topop->larg;
134 while (node && IsA(node, SetOperationStmt))
135 node = ((SetOperationStmt *) node)->larg;
136 Assert(node && IsA(node, RangeTblRef));
137 leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex];
138 leftmostQuery = leftmostRTE->subquery;
139 Assert(leftmostQuery != NULL);
140
141 /*
142 * If the topmost node is a recursive union, it needs special processing.
143 */
144 if (root->hasRecursion)
145 {
146 setop_rel = generate_recursion_path(topop, root,
147 leftmostQuery->targetList,
148 &top_tlist);
149 }
150 else
151 {
152 bool trivial_tlist;
153
154 /*
155 * Recurse on setOperations tree to generate paths for set ops. The
156 * final output paths should have just the column types shown as the
157 * output from the top-level node.
158 */
159 setop_rel = recurse_set_operations((Node *) topop, root,
160 NULL, /* no parent */
161 topop->colTypes, topop->colCollations,
162 leftmostQuery->targetList,
163 &top_tlist,
164 &trivial_tlist);
165 }
166
167 /* Must return the built tlist into root->processed_tlist. */
168 root->processed_tlist = top_tlist;
169
170 return setop_rel;
171}
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
static RelOptInfo * generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition: prepunion.c:357
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:717
void setup_simple_rel_arrays(PlannerInfo *root)
Definition: relnode.c:94
Query * subquery
Definition: parsenodes.h:1118

References Assert(), castNode, generate_recursion_path(), IsA, SetOperationStmt::larg, NIL, parse(), recurse_set_operations(), root, setup_simple_rel_arrays(), RangeTblEntry::subquery, and Query::targetList.

Referenced by grouping_planner().

◆ plan_union_children()

static List * plan_union_children ( PlannerInfo root,
SetOperationStmt top_union,
List refnames_tlist,
List **  tlist_list,
List **  istrivial_tlist 
)
static

Definition at line 1269 of file prepunion.c.

1274{
1275 List *pending_rels = list_make1(top_union);
1276 List *result = NIL;
1277 List *child_tlist;
1278 bool trivial_tlist;
1279
1280 *tlist_list = NIL;
1281 *istrivial_tlist = NIL;
1282
1283 while (pending_rels != NIL)
1284 {
1285 Node *setOp = linitial(pending_rels);
1286
1287 pending_rels = list_delete_first(pending_rels);
1288
1289 if (IsA(setOp, SetOperationStmt))
1290 {
1291 SetOperationStmt *op = (SetOperationStmt *) setOp;
1292
1293 if (op->op == top_union->op &&
1294 (op->all == top_union->all || op->all) &&
1295 equal(op->colTypes, top_union->colTypes) &&
1296 equal(op->colCollations, top_union->colCollations))
1297 {
1298 /* Same UNION, so fold children into parent */
1299 pending_rels = lcons(op->rarg, pending_rels);
1300 pending_rels = lcons(op->larg, pending_rels);
1301 continue;
1302 }
1303 }
1304
1305 /*
1306 * Not same, so plan this child separately.
1307 *
1308 * If top_union isn't a UNION ALL, then we are interested in sorted
1309 * output from the child, so pass top_union as parentOp. Note that
1310 * this isn't necessarily the child node's immediate SetOperationStmt
1311 * parent, but that's fine: it's the effective parent.
1312 */
1313 result = lappend(result, recurse_set_operations(setOp, root,
1314 top_union->all ? NULL : top_union,
1315 top_union->colTypes,
1316 top_union->colCollations,
1317 refnames_tlist,
1318 &child_tlist,
1319 &trivial_tlist));
1320 *tlist_list = lappend(*tlist_list, child_tlist);
1321 *istrivial_tlist = lappend_int(*istrivial_tlist, trivial_tlist);
1322 }
1323
1324 return result;
1325}
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
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
#define list_make1(x1)
Definition: pg_list.h:212

References SetOperationStmt::all, equal(), IsA, lappend(), lappend_int(), SetOperationStmt::larg, lcons(), linitial, list_delete_first(), list_make1, NIL, SetOperationStmt::op, SetOperationStmt::rarg, recurse_set_operations(), and root.

Referenced by generate_union_paths().

◆ postprocess_setop_rel()

static void postprocess_setop_rel ( PlannerInfo root,
RelOptInfo rel 
)
static

Definition at line 1331 of file prepunion.c.

1332{
1333 /*
1334 * We don't currently worry about allowing FDWs to contribute paths to
1335 * this relation, but give extensions a chance.
1336 */
1338 (*create_upper_paths_hook) (root, UPPERREL_SETOP,
1339 NULL, rel, NULL);
1340
1341 /* Select cheapest path */
1342 set_cheapest(rel);
1343}
void set_cheapest(RelOptInfo *parent_rel)
Definition: pathnode.c:269
create_upper_paths_hook_type create_upper_paths_hook
Definition: planner.c:76

References create_upper_paths_hook, root, set_cheapest(), and UPPERREL_SETOP.

Referenced by build_setop_child_paths(), generate_recursion_path(), and recurse_set_operations().

◆ recurse_set_operations()

static RelOptInfo * recurse_set_operations ( Node setOp,
PlannerInfo root,
SetOperationStmt parentOp,
List colTypes,
List colCollations,
List refnames_tlist,
List **  pTargetList,
bool *  istrivial_tlist 
)
static

Definition at line 209 of file prepunion.c.

215{
216 RelOptInfo *rel;
217
218 *istrivial_tlist = true; /* for now */
219
220 /* Guard against stack overflow due to overly complex setop nests */
222
223 if (IsA(setOp, RangeTblRef))
224 {
225 RangeTblRef *rtr = (RangeTblRef *) setOp;
226 RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
227 Query *subquery = rte->subquery;
228 PlannerInfo *subroot;
229 List *tlist;
230 bool trivial_tlist;
231
232 Assert(subquery != NULL);
233
234 /* Build a RelOptInfo for this leaf subquery. */
235 rel = build_simple_rel(root, rtr->rtindex, NULL);
236
237 /* plan_params should not be in use in current query level */
238 Assert(root->plan_params == NIL);
239
240 /*
241 * Generate a subroot and Paths for the subquery. If we have a
242 * parentOp, pass that down to encourage subquery_planner to consider
243 * suitably-sorted Paths.
244 */
245 subroot = rel->subroot = subquery_planner(root->glob, subquery, root,
246 false, root->tuple_fraction,
247 parentOp);
248
249 /*
250 * It should not be possible for the primitive query to contain any
251 * cross-references to other primitive queries in the setop tree.
252 */
253 if (root->plan_params)
254 elog(ERROR, "unexpected outer reference in set operation subquery");
255
256 /* Figure out the appropriate target list for this subquery. */
257 tlist = generate_setop_tlist(colTypes, colCollations,
258 rtr->rtindex,
259 true,
260 subroot->processed_tlist,
261 refnames_tlist,
262 &trivial_tlist);
263 rel->reltarget = create_pathtarget(root, tlist);
264
265 /* Return the fully-fledged tlist to caller, too */
266 *pTargetList = tlist;
267 *istrivial_tlist = trivial_tlist;
268 }
269 else if (IsA(setOp, SetOperationStmt))
270 {
271 SetOperationStmt *op = (SetOperationStmt *) setOp;
272
273 /* UNIONs are much different from INTERSECT/EXCEPT */
274 if (op->op == SETOP_UNION)
275 rel = generate_union_paths(op, root,
276 refnames_tlist,
277 pTargetList);
278 else
280 refnames_tlist,
281 pTargetList);
282
283 /*
284 * If necessary, add a Result node to project the caller-requested
285 * output columns.
286 *
287 * XXX you don't really want to know about this: setrefs.c will apply
288 * fix_upper_expr() to the Result node's tlist. This would fail if the
289 * Vars generated by generate_setop_tlist() were not exactly equal()
290 * to the corresponding tlist entries of the subplan. However, since
291 * the subplan was generated by generate_union_paths() or
292 * generate_nonunion_paths(), and hence its tlist was generated by
293 * generate_append_tlist() or generate_setop_tlist(), this will work.
294 * We just tell generate_setop_tlist() to use varno 0.
295 */
296 if (!tlist_same_datatypes(*pTargetList, colTypes, false) ||
297 !tlist_same_collations(*pTargetList, colCollations, false))
298 {
299 PathTarget *target;
300 bool trivial_tlist;
301 ListCell *lc;
302
303 *pTargetList = generate_setop_tlist(colTypes, colCollations,
304 0,
305 false,
306 *pTargetList,
307 refnames_tlist,
308 &trivial_tlist);
309 *istrivial_tlist = trivial_tlist;
310 target = create_pathtarget(root, *pTargetList);
311
312 /* Apply projection to each path */
313 foreach(lc, rel->pathlist)
314 {
315 Path *subpath = (Path *) lfirst(lc);
316 Path *path;
317
318 Assert(subpath->param_info == NULL);
319 path = apply_projection_to_path(root, subpath->parent,
320 subpath, target);
321 /* If we had to add a Result, path is different from subpath */
322 if (path != subpath)
323 lfirst(lc) = path;
324 }
325
326 /* Apply projection to each partial path */
327 foreach(lc, rel->partial_pathlist)
328 {
329 Path *subpath = (Path *) lfirst(lc);
330 Path *path;
331
332 Assert(subpath->param_info == NULL);
333
334 /* avoid apply_projection_to_path, in case of multiple refs */
335 path = (Path *) create_projection_path(root, subpath->parent,
336 subpath, target);
337 lfirst(lc) = path;
338 }
339 }
341 }
342 else
343 {
344 elog(ERROR, "unrecognized node type: %d",
345 (int) nodeTag(setOp));
346 *pTargetList = NIL;
347 rel = NULL; /* keep compiler quiet */
348 }
349
350 return rel;
351}
#define nodeTag(nodeptr)
Definition: nodes.h:139
ProjectionPath * create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target)
Definition: pathnode.c:2763
Path * apply_projection_to_path(PlannerInfo *root, RelOptInfo *rel, Path *path, PathTarget *target)
Definition: pathnode.c:2873
PlannerInfo * subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, bool hasRecursion, double tuple_fraction, SetOperationStmt *setops)
Definition: planner.c:647
static RelOptInfo * generate_union_paths(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition: prepunion.c:676
static RelOptInfo * generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition: prepunion.c:998
RelOptInfo * build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
Definition: relnode.c:192
void check_stack_depth(void)
Definition: stack_depth.c:95
List * processed_tlist
Definition: pathnodes.h:486
bool tlist_same_collations(List *tlist, List *colCollations, bool junkOK)
Definition: tlist.c:282
bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
Definition: tlist.c:248

References apply_projection_to_path(), Assert(), build_simple_rel(), check_stack_depth(), create_pathtarget, create_projection_path(), elog, ERROR, generate_nonunion_paths(), generate_setop_tlist(), generate_union_paths(), IsA, lfirst, NIL, nodeTag, SetOperationStmt::op, RelOptInfo::partial_pathlist, RelOptInfo::pathlist, postprocess_setop_rel(), PlannerInfo::processed_tlist, RelOptInfo::reltarget, root, RangeTblRef::rtindex, SETOP_UNION, subpath(), RangeTblEntry::subquery, subquery_planner(), RelOptInfo::subroot, tlist_same_collations(), and tlist_same_datatypes().

Referenced by generate_nonunion_paths(), generate_recursion_path(), plan_set_operations(), and plan_union_children().