PostgreSQL Source Code git master
Loading...
Searching...
No Matches
prepunion.c File Reference
#include "postgres.h"
#include <math.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 "port/pg_bitutils.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)
 
static PathTargetcreate_setop_pathtarget (PlannerInfo *root, List *tlist, List *child_pathlist)
 
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 489 of file prepunion.c.

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}
#define bms_is_empty(a)
Definition bitmapset.h:118
#define Assert(condition)
Definition c.h:945
void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition costsize.c:6046
bool enable_incremental_sort
Definition costsize.c:152
void add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel, List *child_tlist, List *setop_pathkeys)
bool is_dummy_rel(RelOptInfo *rel)
Definition joinrels.c:1464
void mark_dummy_rel(RelOptInfo *rel)
Definition joinrels.c:1513
Datum subpath(PG_FUNCTION_ARGS)
Definition ltree_op.c:311
@ RTE_SUBQUERY
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: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
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition pathnode.c:459
@ UPPERREL_FINAL
Definition pathnodes.h:152
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
#define linitial(l)
Definition pg_list.h:178
static int fb(int x)
static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
Definition prepunion.c:1463
tree ctl root
Definition radixtree.h:1857
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition relnode.c:1617
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset, EstimationInfo *estinfo)
Definition selfuncs.c:3788
Definition pg_list.h:54
Cardinality rows
Definition pathnodes.h:1993
bool hasHavingQual
Definition pathnodes.h:621
Query * parse
Definition pathnodes.h:309
Cardinality limit_tuples
Definition pathnodes.h:608
List * setop_pathkeys
Definition pathnodes.h:523
List * groupClause
Definition parsenodes.h:216
List * targetList
Definition parsenodes.h:198
List * groupingSets
Definition parsenodes.h:220
List * distinctClause
Definition parsenodes.h:226
bool consider_parallel
Definition pathnodes.h:1025
Relids lateral_relids
Definition pathnodes.h:1052
struct Path * cheapest_total_path
Definition pathnodes.h:1042
PlannerInfo * subroot
Definition pathnodes.h:1088
RTEKind rtekind
Definition pathnodes.h:1061
List * make_tlist_from_pathtarget(PathTarget *target)
Definition tlist.c:633
List * get_tlist_exprs(List *tlist, bool includeJunk)
Definition tlist.c:172

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(), fb(), fetch_upper_rel(), get_tlist_exprs(), Query::groupClause, Query::groupingSets, PlannerInfo::hasHavingQual, is_dummy_rel(), RelOptInfo::lateral_relids, lfirst, PlannerInfo::limit_tuples, linitial, make_tlist_from_pathtarget(), mark_dummy_rel(), NIL, PlannerInfo::parse, pathkeys_count_contained_in(), 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().

◆ create_setop_pathtarget()

static PathTarget * create_setop_pathtarget ( PlannerInfo root,
List tlist,
List child_pathlist 
)
static

Definition at line 1766 of file prepunion.c.

1767{
1768 PathTarget *reltarget;
1769 ListCell *lc;
1770 double parent_rows = 0;
1771 double parent_size = 0;
1772
1773 reltarget = create_pathtarget(root, tlist);
1774
1775 /* Calculate the total rows and total size. */
1776 foreach(lc, child_pathlist)
1777 {
1778 Path *path = (Path *) lfirst(lc);
1779
1780 parent_rows += path->rows;
1781 parent_size += path->parent->reltarget->width * path->rows;
1782 }
1783
1784 if (parent_rows > 0)
1785 reltarget->width = rint(parent_size / parent_rows);
1786
1787 return reltarget;
1788}
#define create_pathtarget(root, tlist)
Definition tlist.h:58

References create_pathtarget, fb(), lfirst, root, Path::rows, and PathTarget::width.

Referenced by generate_nonunion_paths(), 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 1617 of file prepunion.c.

1620{
1621 List *tlist = NIL;
1622 int resno = 1;
1626 int colindex;
1628 Node *expr;
1631
1632 /*
1633 * First extract typmods to use.
1634 *
1635 * If the inputs all agree on type and typmod of a particular column, use
1636 * that typmod; else use -1.
1637 */
1639
1640 foreach(tlistl, input_tlists)
1641 {
1642 List *subtlist = (List *) lfirst(tlistl);
1644
1646 colindex = 0;
1647 foreach(subtlistl, subtlist)
1648 {
1650
1651 Assert(!subtle->resjunk);
1653 if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1654 {
1655 /* If first subplan, copy the typmod; else compare */
1656 int32 subtypmod = exprTypmod((Node *) subtle->expr);
1657
1660 else if (subtypmod != colTypmods[colindex])
1661 colTypmods[colindex] = -1;
1662 }
1663 else
1664 {
1665 /* types disagree, so force typmod to -1 */
1666 colTypmods[colindex] = -1;
1667 }
1669 colindex++;
1670 }
1672 }
1673
1674 /*
1675 * Now we can build the tlist for the Append.
1676 */
1677 colindex = 0;
1680 {
1685
1686 Assert(reftle->resno == resno);
1687 Assert(!reftle->resjunk);
1688 expr = (Node *) makeVar(0,
1689 resno,
1690 colType,
1691 colTypmod,
1692 colColl,
1693 0);
1694 tle = makeTargetEntry((Expr *) expr,
1695 (AttrNumber) resno++,
1696 pstrdup(reftle->resname),
1697 false);
1698
1699 /*
1700 * By convention, all output columns in a setop tree have
1701 * ressortgroupref equal to their resno. In some cases the ref isn't
1702 * needed, but this is a cleaner way than modifying the tlist later.
1703 */
1704 tle->ressortgroupref = tle->resno;
1705
1706 tlist = lappend(tlist, tle);
1707 }
1708
1710
1711 return tlist;
1712}
int16 AttrNumber
Definition attnum.h:21
int32_t int32
Definition c.h:614
#define palloc_array(type, count)
Definition fe_memutils.h:76
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:1781
void pfree(void *pointer)
Definition mcxt.c:1616
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
int32 exprTypmod(const Node *expr)
Definition nodeFuncs.c:304
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 nodes.h:135

References Assert, exprType(), exprTypmod(), fb(), forthree, lappend(), lfirst, lfirst_oid, list_head(), list_length(), lnext(), makeTargetEntry(), makeVar(), NIL, palloc_array, pfree(), and pstrdup().

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 1052 of file prepunion.c.

1055{
1058 *rrel;
1059 double save_fraction = root->tuple_fraction;
1060 Path *lpath,
1061 *rpath,
1062 *path;
1064 *rpath_tlist,
1065 *tlist,
1066 *groupList;
1071 double dLeftGroups,
1073 dNumGroups,
1075 bool can_sort;
1076 bool can_hash;
1077 SetOpCmd cmd;
1078
1079 /*
1080 * Tell children to fetch all tuples.
1081 */
1082 root->tuple_fraction = 0.0;
1083
1084 /* Recurse on children */
1086 op,
1087 op->colTypes, op->colCollations,
1089 &lpath_tlist,
1091
1093 op,
1094 op->colTypes, op->colCollations,
1096 &rpath_tlist,
1098
1099 /*
1100 * Generate tlist for SetOp plan node.
1101 *
1102 * The tlist for a SetOp plan isn't important so far as the SetOp is
1103 * concerned, but we must make it look real anyway for the benefit of the
1104 * next plan level up.
1105 */
1106 tlist = generate_setop_tlist(op->colTypes, op->colCollations,
1107 0, false, lpath_tlist, refnames_tlist,
1109
1110 /* We should not have needed any type coercions in the tlist */
1112
1113 *pTargetList = tlist;
1114
1115 /* Identify the grouping semantics */
1116 groupList = generate_setop_grouplist(op, tlist);
1117
1118 /* Check whether the operators support sorting or hashing */
1119 can_sort = grouping_is_sortable(groupList);
1120 can_hash = grouping_is_hashable(groupList);
1121 if (!can_sort && !can_hash)
1122 ereport(ERROR,
1124 /* translator: %s is INTERSECT or EXCEPT */
1125 errmsg("could not implement %s",
1126 (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT"),
1127 errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
1128
1129 if (can_sort)
1130 {
1131 /* Determine the pathkeys for sorting by the whole target list */
1133 tlist);
1134
1135 root->query_pathkeys = nonunion_pathkeys;
1136 }
1137
1138 /*
1139 * Now that we've got all that info, we can build the child paths.
1140 */
1141 if (lrel->rtekind == RTE_SUBQUERY)
1144 else
1145 dLeftGroups = lrel->rows;
1146 if (rrel->rtekind == RTE_SUBQUERY)
1149 else
1150 dRightGroups = rrel->rows;
1151
1152 /* Undo effects of forcing tuple_fraction to 0 */
1153 root->tuple_fraction = save_fraction;
1154
1155 /*
1156 * For EXCEPT, we must put the left input first. For INTERSECT, either
1157 * order should give the same results, and we prefer to put the smaller
1158 * input first in order to (a) minimize the size of the hash table in the
1159 * hashing case, and (b) improve our chances of exploiting the executor's
1160 * fast path for empty left-hand input. "Smaller" means the one with the
1161 * fewer groups.
1162 */
1163 if (op->op != SETOP_EXCEPT && dLeftGroups > dRightGroups)
1164 {
1165 /* need to swap the two inputs */
1167 List *tmplist;
1168 double tmpd;
1169
1170 tmprel = lrel;
1171 lrel = rrel;
1172 rrel = tmprel;
1176 tmpd = dLeftGroups;
1179 }
1180
1181 lpath = lrel->cheapest_total_path;
1182 rpath = rrel->cheapest_total_path;
1183
1184 /* Build result relation. */
1186 bms_union(lrel->relids, rrel->relids));
1187
1188 /*
1189 * Create the PathTarget and set the width accordingly. For EXCEPT, since
1190 * the set op result won't contain rows from the rpath, we only account
1191 * for the width of the lpath. For INTERSECT, use both input paths.
1192 */
1193 if (op->op == SETOP_EXCEPT)
1194 result_rel->reltarget = create_setop_pathtarget(root, tlist,
1195 list_make1(lpath));
1196 else
1197 result_rel->reltarget = create_setop_pathtarget(root, tlist,
1198 list_make2(lpath, rpath));
1199
1200 /* Check for provably empty setop inputs and add short-circuit paths. */
1201 if (op->op == SETOP_EXCEPT)
1202 {
1203 /*
1204 * For EXCEPTs, if the left side is dummy then there's no need to
1205 * inspect the right-hand side as scanning the right to find tuples to
1206 * remove won't make the left-hand input any more empty.
1207 */
1208 if (is_dummy_rel(lrel))
1209 {
1211
1212 return result_rel;
1213 }
1214
1215 /* Handle EXCEPTs with dummy right input */
1216 if (is_dummy_rel(rrel))
1217 {
1218 if (op->all)
1219 {
1220 Path *apath;
1221 AppendPathInput append = {0};
1222
1224
1225 /*
1226 * EXCEPT ALL: If the right-hand input is dummy then we can
1227 * simply scan the left-hand input. To keep createplan.c
1228 * happy, use a single child Append to handle the translation
1229 * between the set op targetlist and the targetlist of the
1230 * left input. The Append will be removed in setrefs.c.
1231 */
1233 append, NIL, NULL, 0,
1234 false, -1);
1235
1237
1238 return result_rel;
1239 }
1240 else
1241 {
1242 /*
1243 * To make EXCEPT with a dummy RHS work means having to
1244 * deduplicate the left input. That could be done with
1245 * AggPaths, but it doesn't seem worth the effort. Let the
1246 * normal path generation code below handle this one.
1247 */
1248 }
1249 }
1250 }
1251 else
1252 {
1253 /*
1254 * For INTERSECT, if either input is a dummy rel then we can mark the
1255 * result_rel as dummy since intersecting with an empty relation can
1256 * never yield any results. This is true regardless of INTERSECT or
1257 * INTERSECT ALL.
1258 */
1260 {
1262
1263 return result_rel;
1264 }
1265 }
1266
1267 /*
1268 * Estimate number of distinct groups that we'll need hashtable entries
1269 * for; this is the size of the left-hand input for EXCEPT, or the smaller
1270 * input for INTERSECT. Also estimate the number of eventual output rows.
1271 * In non-ALL cases, we estimate each group produces one output row; in
1272 * ALL cases use the relevant relation size. These are worst-case
1273 * estimates, of course, but we need to be conservative.
1274 */
1275 if (op->op == SETOP_EXCEPT)
1276 {
1278 dNumOutputRows = op->all ? lpath->rows : dNumGroups;
1279 }
1280 else
1281 {
1283 dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
1284 }
1285 result_rel->rows = dNumOutputRows;
1286
1287 /* Select the SetOpCmd type */
1288 switch (op->op)
1289 {
1290 case SETOP_INTERSECT:
1292 break;
1293 case SETOP_EXCEPT:
1295 break;
1296 default:
1297 elog(ERROR, "unrecognized set op: %d", (int) op->op);
1298 cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */
1299 break;
1300 }
1301
1302 /*
1303 * If we can hash, that just requires a SetOp atop the cheapest inputs.
1304 */
1305 if (can_hash)
1306 {
1307 path = (Path *) create_setop_path(root,
1308 result_rel,
1309 lpath,
1310 rpath,
1311 cmd,
1313 groupList,
1314 dNumGroups,
1316 add_path(result_rel, path);
1317 }
1318
1319 /*
1320 * If we can sort, generate the cheapest sorted input paths, and add a
1321 * SetOp atop those.
1322 */
1323 if (can_sort)
1324 {
1325 List *pathkeys;
1326 Path *slpath,
1327 *srpath;
1328
1329 /* First the left input ... */
1331 groupList,
1332 lpath_tlist);
1333 if (pathkeys_contained_in(pathkeys, lpath->pathkeys))
1334 slpath = lpath; /* cheapest path is already sorted */
1335 else
1336 {
1339 NULL,
1340 TOTAL_COST,
1341 false);
1342 /* Subquery failed to produce any presorted paths? */
1343 if (slpath == NULL)
1345 lpath->parent,
1346 lpath,
1347 pathkeys,
1348 -1.0);
1349 }
1350
1351 /* and now the same for the right. */
1353 groupList,
1354 rpath_tlist);
1355 if (pathkeys_contained_in(pathkeys, rpath->pathkeys))
1356 srpath = rpath; /* cheapest path is already sorted */
1357 else
1358 {
1361 NULL,
1362 TOTAL_COST,
1363 false);
1364 /* Subquery failed to produce any presorted paths? */
1365 if (srpath == NULL)
1367 rpath->parent,
1368 rpath,
1369 pathkeys,
1370 -1.0);
1371 }
1372
1373 path = (Path *) create_setop_path(root,
1374 result_rel,
1375 slpath,
1376 srpath,
1377 cmd,
1379 groupList,
1380 dNumGroups,
1382 add_path(result_rel, path);
1383 }
1384
1385 return result_rel;
1386}
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:251
#define Min(x, y)
Definition c.h:1093
int errcode(int sqlerrcode)
Definition elog.c:874
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
SetOpCmd
Definition nodes.h:407
@ SETOPCMD_EXCEPT
Definition nodes.h:410
@ SETOPCMD_EXCEPT_ALL
Definition nodes.h:411
@ SETOPCMD_INTERSECT_ALL
Definition nodes.h:409
@ SETOPCMD_INTERSECT
Definition nodes.h:408
@ SETOP_HASHED
Definition nodes.h:417
@ SETOP_SORTED
Definition nodes.h:416
static char * errmsg
@ SETOP_INTERSECT
@ SETOP_EXCEPT
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:3466
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
@ TOTAL_COST
Definition pathnodes.h:111
@ UPPERREL_SETOP
Definition pathnodes.h:144
#define list_make1(x1)
Definition pg_list.h:212
#define list_make2(x1, x2)
Definition pg_list.h:214
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition prepunion.c:1726
static PathTarget * create_setop_pathtarget(PlannerInfo *root, List *tlist, List *child_pathlist)
Definition prepunion.c:1766
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:1489
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 void build_setop_child_paths(PlannerInfo *root, RelOptInfo *rel, bool trivial_tlist, List *child_tlist, List *interesting_pathkeys, double *pNumGroups)
Definition prepunion.c:489
List * subpaths
Definition pathnode.h:36
List * pathkeys
Definition pathnodes.h:1999
SetOperation op
bool grouping_is_sortable(List *groupClause)
Definition tlist.c:549
bool grouping_is_hashable(List *groupClause)
Definition tlist.c:569

References add_path(), SetOperationStmt::all, Assert, bms_union(), build_setop_child_paths(), create_append_path(), create_setop_path(), create_setop_pathtarget(), create_sort_path(), elog, ereport, errcode(), errdetail(), errmsg, ERROR, fb(), fetch_upper_rel(), generate_setop_grouplist(), generate_setop_tlist(), get_cheapest_path_for_pathkeys(), grouping_is_hashable(), grouping_is_sortable(), is_dummy_rel(), SetOperationStmt::larg, list_make1, list_make2, make_pathkeys_for_sortclauses(), mark_dummy_rel(), Min, NIL, SetOperationStmt::op, Path::pathkeys, pathkeys_contained_in(), SetOperationStmt::rarg, recurse_set_operations(), root, Path::rows, RTE_SUBQUERY, SETOP_EXCEPT, SETOP_HASHED, SETOP_INTERSECT, SETOP_SORTED, SETOPCMD_EXCEPT, SETOPCMD_EXCEPT_ALL, SETOPCMD_INTERSECT, SETOPCMD_INTERSECT_ALL, AppendPathInput::subpaths, 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 365 of file prepunion.c.

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}
@ SETOP_UNION
RecursiveUnionPath * create_recursiveunion_path(PlannerInfo *root, RelOptInfo *rel, Path *leftpath, Path *rightpath, PathTarget *target, List *distinctList, int wtParam, double numGroups)
Definition pathnode.c:3585
static List * generate_append_tlist(List *colTypes, List *colCollations, List *input_tlists, List *refnames_tlist)
Definition prepunion.c:1617

References add_path(), Assert, bms_union(), build_setop_child_paths(), create_pathtarget, create_recursiveunion_path(), elog, ereport, errcode(), errdetail(), errmsg, ERROR, fb(), fetch_upper_rel(), generate_append_tlist(), generate_setop_grouplist(), grouping_is_hashable(), list_make2, NIL, postprocess_setop_rel(), recurse_set_operations(), root, Path::rows, RTE_SUBQUERY, 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 1726 of file prepunion.c.

1727{
1728 List *grouplist = copyObject(op->groupClauses);
1729 ListCell *lg;
1730 ListCell *lt;
1731
1733 foreach(lt, targetlist)
1734 {
1735 TargetEntry *tle = (TargetEntry *) lfirst(lt);
1737
1738 Assert(!tle->resjunk);
1739
1740 /* non-resjunk columns should have sortgroupref = resno */
1741 Assert(tle->ressortgroupref == tle->resno);
1742
1743 /* non-resjunk columns should have grouping clauses */
1744 Assert(lg != NULL);
1746 lg = lnext(grouplist, lg);
1747 Assert(sgc->tleSortGroupRef == 0);
1748
1749 sgc->tleSortGroupRef = tle->ressortgroupref;
1750 }
1751 Assert(lg == NULL);
1752 return grouplist;
1753}
#define copyObject(obj)
Definition nodes.h:232

References Assert, copyObject, fb(), lfirst, list_head(), and lnext().

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 1489 of file prepunion.c.

1495{
1496 List *tlist = NIL;
1497 int resno = 1;
1498 ListCell *ctlc,
1499 *cclc,
1500 *itlc,
1501 *rtlc;
1503 Node *expr;
1504
1505 *trivial_tlist = true; /* until proven differently */
1506
1509 {
1514
1515 Assert(inputtle->resno == resno);
1516 Assert(reftle->resno == resno);
1517 Assert(!inputtle->resjunk);
1518 Assert(!reftle->resjunk);
1519
1520 /*
1521 * Generate columns referencing input columns and having appropriate
1522 * data types and column names. Insert datatype coercions where
1523 * necessary.
1524 *
1525 * HACK: constants in the input's targetlist are copied up as-is
1526 * rather than being referenced as subquery outputs. This is mainly
1527 * to ensure that when we try to coerce them to the output column's
1528 * datatype, the right things happen for UNKNOWN constants. But do
1529 * this only at the first level of subquery-scan plans; we don't want
1530 * phony constants appearing in the output tlists of upper-level
1531 * nodes!
1532 *
1533 * Note that copying a constant doesn't in itself require us to mark
1534 * the tlist nontrivial; see trivial_subqueryscan() in setrefs.c.
1535 */
1536 if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
1537 expr = (Node *) inputtle->expr;
1538 else
1539 expr = (Node *) makeVar(varno,
1540 inputtle->resno,
1541 exprType((Node *) inputtle->expr),
1542 exprTypmod((Node *) inputtle->expr),
1543 exprCollation((Node *) inputtle->expr),
1544 0);
1545
1546 if (exprType(expr) != colType)
1547 {
1548 /*
1549 * Note: it's not really cool to be applying coerce_to_common_type
1550 * here; one notable point is that assign_expr_collations never
1551 * gets run on any generated nodes. For the moment that's not a
1552 * problem because we force the correct exposed collation below.
1553 * It would likely be best to make the parser generate the correct
1554 * output tlist for every set-op to begin with, though.
1555 */
1556 expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */
1557 expr,
1558 colType,
1559 "UNION/INTERSECT/EXCEPT");
1560 *trivial_tlist = false; /* the coercion makes it not trivial */
1561 }
1562
1563 /*
1564 * Ensure the tlist entry's exposed collation matches the set-op. This
1565 * is necessary because plan_set_operations() reports the result
1566 * ordering as a list of SortGroupClauses, which don't carry collation
1567 * themselves but just refer to tlist entries. If we don't show the
1568 * right collation then planner.c might do the wrong thing in
1569 * higher-level queries.
1570 *
1571 * Note we use RelabelType, not CollateExpr, since this expression
1572 * will reach the executor without any further processing.
1573 */
1574 if (exprCollation(expr) != colColl)
1575 {
1576 expr = applyRelabelType(expr,
1577 exprType(expr), exprTypmod(expr), colColl,
1578 COERCE_IMPLICIT_CAST, -1, false);
1579 *trivial_tlist = false; /* the relabel makes it not trivial */
1580 }
1581
1582 tle = makeTargetEntry((Expr *) expr,
1583 (AttrNumber) resno++,
1584 pstrdup(reftle->resname),
1585 false);
1586
1587 /*
1588 * By convention, all output columns in a setop tree have
1589 * ressortgroupref equal to their resno. In some cases the ref isn't
1590 * needed, but this is a cleaner way than modifying the tlist later.
1591 */
1592 tle->ressortgroupref = tle->resno;
1593
1594 tlist = lappend(tlist, tle);
1595 }
1596
1597 return tlist;
1598}
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
#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:769

References applyRelabelType(), Assert, COERCE_IMPLICIT_CAST, coerce_to_common_type(), exprCollation(), exprType(), exprTypmod(), fb(), forfour, IsA, lappend(), lfirst, lfirst_oid, makeTargetEntry(), makeVar(), NIL, and pstrdup().

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 691 of file prepunion.c.

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
715 /*
716 * If any of my children are identical UNION nodes (same op, all-flag, and
717 * colTypes/colCollations) then they can be merged into this node so that
718 * we generate only one Append/MergeAppend and unique-ification for the
719 * lot. Recurse to find such nodes.
720 */
722 op,
724 &tlist_list,
726
727 /*
728 * Generate tlist for Append/MergeAppend plan node.
729 *
730 * The tlist for an Append plan isn't important as far as the Append is
731 * concerned, but we must make it look real anyway for the benefit of the
732 * next plan level up.
733 */
734 tlist = generate_append_tlist(op->colTypes, op->colCollations,
736 *pTargetList = tlist;
737
738 /* For UNIONs (not UNION ALL), try sorting, if sorting is possible */
739 if (!op->all)
740 {
741 /* Identify the grouping semantics */
742 groupList = generate_setop_grouplist(op, tlist);
743
744 if (grouping_is_sortable(op->groupClauses))
745 {
746 try_sorted = true;
747 /* Determine the pathkeys for sorting by the whole target list */
749 tlist);
750
751 root->query_pathkeys = union_pathkeys;
752 }
753 }
754
755 /*
756 * Now that we've got the append target list, we can build the union child
757 * paths.
758 */
760 {
761 RelOptInfo *rel = lfirst(lc);
764
765 /* only build paths for the union children */
766 if (rel->rtekind == RTE_SUBQUERY)
769 }
770
771 /* Build path lists and relid set. */
772 foreach(lc, rellist)
773 {
774 RelOptInfo *rel = lfirst(lc);
776
777 /*
778 * Record the relids so that we can identify the correct
779 * UPPERREL_SETOP RelOptInfo below.
780 */
781 relids = bms_add_members(relids, rel->relids);
782
783 /* Skip any UNION children that are proven not to yield any rows */
784 if (is_dummy_rel(rel))
785 continue;
786
787 cheapest.subpaths = lappend(cheapest.subpaths,
789
790 if (try_sorted)
791 {
794 NULL,
796 false);
797
798 if (ordered_path != NULL)
799 ordered.subpaths = lappend(ordered.subpaths, ordered_path);
800 else
801 {
802 /*
803 * If we can't find a sorted path, just give up trying to
804 * generate a list of correctly sorted child paths. This can
805 * happen when type coercion was added to the targetlist due
806 * to mismatching types from the union children.
807 */
808 try_sorted = false;
809 }
810 }
811
812 if (consider_parallel)
813 {
814 if (!rel->consider_parallel)
815 {
816 consider_parallel = false;
817 partial_paths_valid = false;
818 }
819 else if (rel->partial_pathlist == NIL)
820 partial_paths_valid = false;
821 else
822 partial.partial_subpaths = lappend(partial.partial_subpaths,
824 }
825 }
826
827 /* Build result relation. */
829 result_rel->reltarget = create_setop_pathtarget(root, tlist,
830 cheapest.subpaths);
831 result_rel->consider_parallel = consider_parallel;
832 result_rel->consider_startup = (root->tuple_fraction > 0);
833
834 /* If all UNION children were dummy rels, make the resulting rel dummy */
835 if (cheapest.subpaths == NIL)
836 {
838
839 return result_rel;
840 }
841
842 /*
843 * Append the child results together using the cheapest paths from each
844 * union child.
845 */
847 NIL, NULL, 0, false, -1);
848
849 /*
850 * Estimate number of groups. For now we just assume the output is unique
851 * --- this is certainly true for the UNION case, and we want worst-case
852 * estimates anyway.
853 */
854 result_rel->rows = apath->rows;
855
856 /*
857 * Now consider doing the same thing using the partial paths plus Append
858 * plus Gather.
859 */
861 {
862 Path *papath;
863 int parallel_workers = 0;
864
865 /* Find the highest number of workers requested for any subpath. */
866 foreach(lc, partial.partial_subpaths)
867 {
868 Path *subpath = lfirst(lc);
869
870 parallel_workers = Max(parallel_workers,
871 subpath->parallel_workers);
872 }
873 Assert(parallel_workers > 0);
874
875 /*
876 * If the use of parallel append is permitted, always request at least
877 * log2(# of children) paths. We assume it can be useful to have
878 * extra workers in this case because they will be spread out across
879 * the children. The precise formula is just a guess; see
880 * add_paths_to_append_rel.
881 */
883 {
884 parallel_workers = Max(parallel_workers,
886 parallel_workers = Min(parallel_workers,
888 }
889 Assert(parallel_workers > 0);
890
891 papath = (Path *)
893 NIL, NULL, parallel_workers,
895 gpath = (Path *)
897 result_rel->reltarget, NULL, NULL);
898 }
899
900 if (!op->all)
901 {
902 double dNumGroups;
903 bool can_sort = grouping_is_sortable(groupList);
904 bool can_hash = grouping_is_hashable(groupList);
905 Path *first_path = linitial(cheapest.subpaths);
906
907 /*
908 * Estimate the number of UNION output rows. In the case when only a
909 * single UNION child remains, we can use estimate_num_groups() on
910 * that child. We must be careful not to do this when that child is
911 * the result of some other set operation as the targetlist will
912 * contain Vars with varno==0, which estimate_num_groups() wouldn't
913 * like.
914 */
915 if (list_length(cheapest.subpaths) == 1 &&
916 first_path->parent->reloptkind != RELOPT_UPPER_REL)
917 {
919 first_path->pathtarget->exprs,
920 first_path->rows,
921 NULL,
922 NULL);
923 }
924 else
925 {
926 /*
927 * Otherwise, for the moment, take the number of distinct groups
928 * as equal to the total input size, i.e., the worst case. This
929 * is too conservative, but it's not clear how to get a decent
930 * estimate of the true size. One should note as well the
931 * propensity of novices to write UNION rather than UNION ALL even
932 * when they don't expect any duplicates...
933 */
934 dNumGroups = apath->rows;
935 }
936
937 if (can_hash)
938 {
939 Path *path;
940
941 /*
942 * Try a hash aggregate plan on 'apath'. This is the cheapest
943 * available path containing each append child.
944 */
945 path = (Path *) create_agg_path(root,
947 apath,
948 result_rel->reltarget,
951 groupList,
952 NIL,
953 NULL,
954 dNumGroups);
955 add_path(result_rel, path);
956
957 /* Try hash aggregate on the Gather path, if valid */
958 if (gpath != NULL)
959 {
960 /* Hashed aggregate plan --- no sort needed */
961 path = (Path *) create_agg_path(root,
963 gpath,
964 result_rel->reltarget,
967 groupList,
968 NIL,
969 NULL,
970 dNumGroups);
971 add_path(result_rel, path);
972 }
973 }
974
975 if (can_sort)
976 {
977 Path *path = apath;
978
979 /* Try Sort -> Unique on the Append path */
980 if (groupList != NIL)
981 path = (Path *) create_sort_path(root, result_rel, path,
982 make_pathkeys_for_sortclauses(root, groupList, tlist),
983 -1.0);
984
985 path = (Path *) create_unique_path(root,
987 path,
988 list_length(path->pathkeys),
989 dNumGroups);
990
991 add_path(result_rel, path);
992
993 /* Try Sort -> Unique on the Gather path, if set */
994 if (gpath != NULL)
995 {
996 path = gpath;
997
998 path = (Path *) create_sort_path(root, result_rel, path,
999 make_pathkeys_for_sortclauses(root, groupList, tlist),
1000 -1.0);
1001
1002 path = (Path *) create_unique_path(root,
1003 result_rel,
1004 path,
1005 list_length(path->pathkeys),
1006 dNumGroups);
1007 add_path(result_rel, path);
1008 }
1009 }
1010
1011 /*
1012 * Try making a MergeAppend path if we managed to find a path with the
1013 * correct pathkeys in each union child query.
1014 */
1015 if (try_sorted && groupList != NIL)
1016 {
1017 Path *path;
1018
1020 result_rel,
1021 ordered.subpaths,
1022 NIL,
1024 NULL);
1025
1026 /* and make the MergeAppend unique */
1027 path = (Path *) create_unique_path(root,
1028 result_rel,
1029 path,
1030 list_length(tlist),
1031 dNumGroups);
1032
1033 add_path(result_rel, path);
1034 }
1035 }
1036 else
1037 {
1038 /* UNION ALL */
1040
1041 if (gpath != NULL)
1043 }
1044
1045 return result_rel;
1046}
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:901
#define Max(x, y)
Definition c.h:1087
int max_parallel_workers_per_gather
Definition costsize.c:144
bool enable_parallel_append
Definition costsize.c:162
@ AGG_HASHED
Definition nodes.h:366
@ AGGSPLIT_SIMPLE
Definition nodes.h:387
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
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:3057
@ RELOPT_UPPER_REL
Definition pathnodes.h:969
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:1401
List * partial_subpaths
Definition pathnode.h:37
Relids relids
Definition pathnodes.h:1009
List * pathlist
Definition pathnodes.h:1038
List * partial_pathlist
Definition pathnodes.h:1040

References add_path(), AGG_HASHED, AGGSPLIT_SIMPLE, SetOperationStmt::all, Assert, bms_add_members(), build_setop_child_paths(), RelOptInfo::cheapest_total_path, RelOptInfo::consider_parallel, create_agg_path(), create_append_path(), create_gather_path(), create_merge_append_path(), create_setop_pathtarget(), create_sort_path(), create_unique_path(), enable_parallel_append, estimate_num_groups(), fb(), fetch_upper_rel(), forthree, generate_append_tlist(), generate_setop_grouplist(), get_cheapest_path_for_pathkeys(), grouping_is_hashable(), grouping_is_sortable(), is_dummy_rel(), lappend(), lfirst, lfirst_int, lfirst_node, linitial, list_length(), make_pathkeys_for_sortclauses(), mark_dummy_rel(), Max, max_parallel_workers_per_gather, Min, NIL, RelOptInfo::partial_pathlist, AppendPathInput::partial_subpaths, Path::pathkeys, RelOptInfo::pathlist, pg_leftmost_one_pos32(), plan_union_children(), RelOptInfo::relids, RELOPT_UPPER_REL, root, RTE_SUBQUERY, RelOptInfo::rtekind, subpath(), AppendPathInput::subpaths, TOTAL_COST, and UPPERREL_SETOP.

Referenced by recurse_set_operations().

◆ plan_set_operations()

RelOptInfo * plan_set_operations ( PlannerInfo root)

Definition at line 98 of file prepunion.c.

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}
void parse(int)
Definition parse.c:49
#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:365
void setup_simple_rel_arrays(PlannerInfo *root)
Definition relnode.c:114

References Assert, castNode, fb(), generate_recursion_path(), IsA, NIL, parse(), recurse_set_operations(), root, and setup_simple_rel_arrays().

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 1401 of file prepunion.c.

1406{
1408 List *result = NIL;
1410 bool trivial_tlist;
1411
1412 *tlist_list = NIL;
1414
1415 while (pending_rels != NIL)
1416 {
1418
1420
1422 {
1424
1425 if (op->op == top_union->op &&
1426 (op->all == top_union->all || op->all) &&
1427 equal(op->colTypes, top_union->colTypes) &&
1428 equal(op->colCollations, top_union->colCollations))
1429 {
1430 /* Same UNION, so fold children into parent */
1433 continue;
1434 }
1435 }
1436
1437 /*
1438 * Not same, so plan this child separately.
1439 *
1440 * If top_union isn't a UNION ALL, then we are interested in sorted
1441 * output from the child, so pass top_union as parentOp. Note that
1442 * this isn't necessarily the child node's immediate SetOperationStmt
1443 * parent, but that's fine: it's the effective parent.
1444 */
1445 result = lappend(result, recurse_set_operations(setOp, root,
1446 top_union->all ? NULL : top_union,
1450 &child_tlist,
1451 &trivial_tlist));
1454 }
1455
1456 return result;
1457}
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

References SetOperationStmt::all, equal(), fb(), 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 1463 of file prepunion.c.

1464{
1465 /*
1466 * We don't currently worry about allowing FDWs to contribute paths to
1467 * this relation, but give extensions a chance.
1468 */
1470 (*create_upper_paths_hook) (root, UPPERREL_SETOP,
1471 NULL, rel, NULL);
1472
1473 /* Select cheapest path */
1474 set_cheapest(rel);
1475}
void set_cheapest(RelOptInfo *parent_rel)
Definition pathnode.c:268
create_upper_paths_hook_type create_upper_paths_hook
Definition planner.c:83

References create_upper_paths_hook, fb(), 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 214 of file prepunion.c.

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,
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}
#define nodeTag(nodeptr)
Definition nodes.h:139
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
char * choose_plan_name(PlannerGlobal *glob, const char *name, bool always_number)
Definition planner.c:9024
PlannerInfo * subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name, PlannerInfo *parent_root, bool hasRecursion, double tuple_fraction, SetOperationStmt *setops)
Definition planner.c:743
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:1052
RelOptInfo * build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
Definition relnode.c:212
void check_stack_depth(void)
Definition stack_depth.c:95
List * processed_tlist
Definition pathnodes.h:581
struct PathTarget * reltarget
Definition pathnodes.h:1033
bool tlist_same_collations(List *tlist, List *colCollations, bool junkOK)
Definition tlist.c:291
bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
Definition tlist.c:257

References apply_projection_to_path(), Assert, build_simple_rel(), check_stack_depth(), choose_plan_name(), create_pathtarget, create_projection_path(), elog, ERROR, fb(), 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, SETOP_UNION, subpath(), 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().