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

491{
493 List *setop_pathkeys = rel->subroot->setop_pathkeys;
494 ListCell *lc;
495
496 /* it can't be a set op child rel if it's not a subquery */
497 Assert(rel->rtekind == RTE_SUBQUERY);
498
499 /* when sorting is needed, add child rel equivalences */
502 rel,
505
506 /*
507 * Mark rel with estimated output rows, width, etc. Note that we have to
508 * do this before generating outer-query paths, else cost_subqueryscan is
509 * not happy.
510 */
512
513 /*
514 * Since we may want to add a partial path to this relation, we must set
515 * its consider_parallel flag correctly.
516 */
518 rel->consider_parallel = final_rel->consider_parallel;
519
520 /* Generate subquery scan paths for any interesting path in final_rel */
521 foreach(lc, final_rel->pathlist)
522 {
523 Path *subpath = (Path *) lfirst(lc);
524 List *pathkeys;
525 Path *cheapest_input_path = final_rel->cheapest_total_path;
526 bool is_sorted;
527 int presorted_keys;
528
529 /* If the input rel is dummy, propagate that to this query level */
531 {
532 mark_dummy_rel(rel);
533 continue;
534 }
535
536 /*
537 * Include the cheapest path as-is so that the set operation can be
538 * cheaply implemented using a method which does not require the input
539 * to be sorted.
540 */
542 {
543 /* Convert subpath's pathkeys to outer representation */
544 pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
546
547 /* Generate outer path using this subpath */
549 rel,
550 subpath,
552 pathkeys,
553 NULL));
554 }
555
556 /* skip dealing with sorted paths if the setop doesn't need them */
558 continue;
559
560 /*
561 * Create paths to suit final sort order required for setop_pathkeys.
562 * Here we'll sort the cheapest input path (if not sorted already) and
563 * incremental sort any paths which are partially sorted.
564 */
565 is_sorted = pathkeys_count_contained_in(setop_pathkeys,
566 subpath->pathkeys,
567 &presorted_keys);
568
569 if (!is_sorted)
570 {
571 double limittuples = rel->subroot->limit_tuples;
572
573 /*
574 * Try at least sorting the cheapest path and also try
575 * incrementally sorting any path which is partially sorted
576 * already (no need to deal with paths which have presorted keys
577 * when incremental sort is disabled unless it's the cheapest
578 * input path).
579 */
581 (presorted_keys == 0 || !enable_incremental_sort))
582 continue;
583
584 /*
585 * We've no need to consider both a sort and incremental sort.
586 * We'll just do a sort if there are no presorted keys and an
587 * incremental sort when there are presorted keys.
588 */
589 if (presorted_keys == 0 || !enable_incremental_sort)
591 final_rel,
592 subpath,
593 setop_pathkeys,
595 else
597 final_rel,
598 subpath,
599 setop_pathkeys,
600 presorted_keys,
602 }
603
604 /*
605 * subpath is now sorted, so add it to the pathlist. We already added
606 * the cheapest_input_path above, so don't add it again unless we just
607 * sorted it.
608 */
610 {
611 /* Convert subpath's pathkeys to outer representation */
612 pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
614
615 /* Generate outer path using this subpath */
617 rel,
618 subpath,
620 pathkeys,
621 NULL));
622 }
623 }
624
625 /* if consider_parallel is false, there should be no partial paths */
626 Assert(final_rel->consider_parallel ||
627 final_rel->partial_pathlist == NIL);
628
629 /*
630 * If we have a partial path for the child relation, we can use that to
631 * build a partial path for this relation. But there's no point in
632 * considering any path but the cheapest.
633 */
635 final_rel->partial_pathlist != NIL)
636 {
639
640 partial_subpath = linitial(final_rel->partial_pathlist);
641 partial_path = (Path *)
644 NIL, NULL);
646 }
647
649
650 /*
651 * Estimate number of groups if caller wants it. If the subquery used
652 * grouping or aggregation, its output is probably mostly unique anyway;
653 * otherwise do statistical estimation.
654 *
655 * XXX you don't really want to know about this: we do the estimation
656 * using the subroot->parse's original targetlist expressions, not the
657 * subroot->processed_tlist which might seem more appropriate. The reason
658 * is that if the subquery is itself a setop, it may return a
659 * processed_tlist containing "varno 0" Vars generated by
660 * generate_append_tlist, and those would confuse estimate_num_groups
661 * mightily. We ought to get rid of the "varno 0" hack, but that requires
662 * a redesign of the parsetree representation of setops, so that there can
663 * be an RTE corresponding to each setop's output. Note, we use this not
664 * subquery's targetlist but subroot->parse's targetlist, because it was
665 * revised by self-join removal. subquery's targetlist might contain the
666 * references to the removed relids.
667 */
668 if (pNumGroups)
669 {
670 PlannerInfo *subroot = rel->subroot;
671 Query *subquery = subroot->parse;
672
673 if (subquery->groupClause || subquery->groupingSets ||
674 subquery->distinctClause || subroot->hasHavingQual ||
675 subquery->hasAggs)
677 else
679 get_tlist_exprs(subroot->parse->targetList, false),
681 NULL,
682 NULL);
683 }
684}
#define bms_is_empty(a)
Definition bitmapset.h:118
#define Assert(condition)
Definition c.h:906
void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition costsize.c:6045
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)
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:794
SubqueryScanPath * create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, bool trivial_pathtarget, List *pathkeys, Relids required_outer)
Definition pathnode.c:1862
IncrementalSortPath * create_incremental_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, int presorted_keys, double limit_tuples)
Definition pathnode.c:2808
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition pathnode.c:2857
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:1462
tree ctl root
Definition radixtree.h:1857
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition relnode.c:1602
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset, EstimationInfo *estinfo)
Definition selfuncs.c:3771
Definition pg_list.h:54
Cardinality rows
Definition pathnodes.h:1991
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 1765 of file prepunion.c.

1766{
1767 PathTarget *reltarget;
1768 ListCell *lc;
1769 double parent_rows = 0;
1770 double parent_size = 0;
1771
1772 reltarget = create_pathtarget(root, tlist);
1773
1774 /* Calculate the total rows and total size. */
1775 foreach(lc, child_pathlist)
1776 {
1777 Path *path = (Path *) lfirst(lc);
1778
1779 parent_rows += path->rows;
1780 parent_size += path->parent->reltarget->width * path->rows;
1781 }
1782
1783 if (parent_rows > 0)
1784 reltarget->width = rint(parent_size / parent_rows);
1785
1786 return reltarget;
1787}
#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 1616 of file prepunion.c.

1619{
1620 List *tlist = NIL;
1621 int resno = 1;
1625 int colindex;
1627 Node *expr;
1630
1631 /*
1632 * First extract typmods to use.
1633 *
1634 * If the inputs all agree on type and typmod of a particular column, use
1635 * that typmod; else use -1.
1636 */
1638
1639 foreach(tlistl, input_tlists)
1640 {
1641 List *subtlist = (List *) lfirst(tlistl);
1643
1645 colindex = 0;
1646 foreach(subtlistl, subtlist)
1647 {
1649
1650 Assert(!subtle->resjunk);
1652 if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1653 {
1654 /* If first subplan, copy the typmod; else compare */
1655 int32 subtypmod = exprTypmod((Node *) subtle->expr);
1656
1659 else if (subtypmod != colTypmods[colindex])
1660 colTypmods[colindex] = -1;
1661 }
1662 else
1663 {
1664 /* types disagree, so force typmod to -1 */
1665 colTypmods[colindex] = -1;
1666 }
1668 colindex++;
1669 }
1671 }
1672
1673 /*
1674 * Now we can build the tlist for the Append.
1675 */
1676 colindex = 0;
1679 {
1684
1685 Assert(reftle->resno == resno);
1686 Assert(!reftle->resjunk);
1687 expr = (Node *) makeVar(0,
1688 resno,
1689 colType,
1690 colTypmod,
1691 colColl,
1692 0);
1693 tle = makeTargetEntry((Expr *) expr,
1694 (AttrNumber) resno++,
1695 pstrdup(reftle->resname),
1696 false);
1697
1698 /*
1699 * By convention, all output columns in a setop tree have
1700 * ressortgroupref equal to their resno. In some cases the ref isn't
1701 * needed, but this is a cleaner way than modifying the tlist later.
1702 */
1703 tle->ressortgroupref = tle->resno;
1704
1705 tlist = lappend(tlist, tle);
1706 }
1707
1709
1710 return tlist;
1711}
int16 AttrNumber
Definition attnum.h:21
int32_t int32
Definition c.h:575
#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: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 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 1051 of file prepunion.c.

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

367{
369 Path *path;
371 *rrel;
372 Path *lpath;
373 Path *rpath;
378 List *tlist;
379 List *groupList;
380 double dNumGroups;
381
382 /* Parser should have rejected other cases */
383 if (setOp->op != SETOP_UNION)
384 elog(ERROR, "only UNION queries can be recursive");
385 /* Worktable ID should be assigned */
386 Assert(root->wt_param_id >= 0);
387
388 /*
389 * Unlike a regular UNION node, process the left and right inputs
390 * separately without any intention of combining them into one Append.
391 */
393 NULL, /* no value in sorted results */
394 setOp->colTypes, setOp->colCollations,
398 if (lrel->rtekind == RTE_SUBQUERY)
400 NIL, NULL);
401 lpath = lrel->cheapest_total_path;
402 /* The right path will want to look at the left one ... */
403 root->non_recursive_path = lpath;
405 NULL, /* no value in sorted results */
406 setOp->colTypes, setOp->colCollations,
410 if (rrel->rtekind == RTE_SUBQUERY)
412 NIL, NULL);
413 rpath = rrel->cheapest_total_path;
414 root->non_recursive_path = NULL;
415
416 /*
417 * Generate tlist for RecursiveUnion path node --- same as in Append cases
418 */
419 tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations,
422
423 *pTargetList = tlist;
424
425 /* Build result relation. */
427 bms_union(lrel->relids, rrel->relids));
428 result_rel->reltarget = create_pathtarget(root, tlist);
429
430 /*
431 * If UNION, identify the grouping operators
432 */
433 if (setOp->all)
434 {
435 groupList = NIL;
436 dNumGroups = 0;
437 }
438 else
439 {
440 /* Identify the grouping semantics */
441 groupList = generate_setop_grouplist(setOp, tlist);
442
443 /* We only support hashing here */
444 if (!grouping_is_hashable(groupList))
447 errmsg("could not implement recursive UNION"),
448 errdetail("All column datatypes must be hashable.")));
449
450 /*
451 * For the moment, take the number of distinct groups as equal to the
452 * total input size, ie, the worst case.
453 */
454 dNumGroups = lpath->rows + rpath->rows * 10;
455 }
456
457 /*
458 * And make the path node.
459 */
462 lpath,
463 rpath,
464 result_rel->reltarget,
465 groupList,
466 root->wt_param_id,
467 dNumGroups);
468
469 add_path(result_rel, path);
471 return result_rel;
472}
@ 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:3538
static List * generate_append_tlist(List *colTypes, List *colCollations, List *input_tlists, List *refnames_tlist)
Definition prepunion.c:1616

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

1726{
1727 List *grouplist = copyObject(op->groupClauses);
1728 ListCell *lg;
1729 ListCell *lt;
1730
1732 foreach(lt, targetlist)
1733 {
1734 TargetEntry *tle = (TargetEntry *) lfirst(lt);
1736
1737 Assert(!tle->resjunk);
1738
1739 /* non-resjunk columns should have sortgroupref = resno */
1740 Assert(tle->ressortgroupref == tle->resno);
1741
1742 /* non-resjunk columns should have grouping clauses */
1743 Assert(lg != NULL);
1745 lg = lnext(grouplist, lg);
1746 Assert(sgc->tleSortGroupRef == 0);
1747
1748 sgc->tleSortGroupRef = tle->ressortgroupref;
1749 }
1750 Assert(lg == NULL);
1751 return grouplist;
1752}
#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 1488 of file prepunion.c.

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

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

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

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

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

1463{
1464 /*
1465 * We don't currently worry about allowing FDWs to contribute paths to
1466 * this relation, but give extensions a chance.
1467 */
1469 (*create_upper_paths_hook) (root, UPPERREL_SETOP,
1470 NULL, rel, NULL);
1471
1472 /* Select cheapest path */
1473 set_cheapest(rel);
1474}
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 213 of file prepunion.c.

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