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:119
#define Assert(condition)
Definition c.h:1002
void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition costsize.c:6070
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:348
@ 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:1455
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:3804
Definition pg_list.h:54
Cardinality rows
Definition pathnodes.h:2005
bool hasHavingQual
Definition pathnodes.h:633
Query * parse
Definition pathnodes.h:309
Cardinality limit_tuples
Definition pathnodes.h:620
List * setop_pathkeys
Definition pathnodes.h:535
List * groupClause
Definition parsenodes.h:221
List * targetList
Definition parsenodes.h:203
List * groupingSets
Definition parsenodes.h:224
List * distinctClause
Definition parsenodes.h:230
bool consider_parallel
Definition pathnodes.h:1037
Relids lateral_relids
Definition pathnodes.h:1064
struct Path * cheapest_total_path
Definition pathnodes.h:1054
PlannerInfo * subroot
Definition pathnodes.h:1100
RTEKind rtekind
Definition pathnodes.h:1073
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 1758 of file prepunion.c.

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

1612{
1613 List *tlist = NIL;
1614 int resno = 1;
1618 int colindex;
1620 Node *expr;
1623
1624 /*
1625 * First extract typmods to use.
1626 *
1627 * If the inputs all agree on type and typmod of a particular column, use
1628 * that typmod; else use -1.
1629 */
1631
1632 foreach(tlistl, input_tlists)
1633 {
1634 List *subtlist = (List *) lfirst(tlistl);
1636
1638 colindex = 0;
1639 foreach(subtlistl, subtlist)
1640 {
1642
1643 Assert(!subtle->resjunk);
1645 if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1646 {
1647 /* If first subplan, copy the typmod; else compare */
1648 int32 subtypmod = exprTypmod((Node *) subtle->expr);
1649
1652 else if (subtypmod != colTypmods[colindex])
1653 colTypmods[colindex] = -1;
1654 }
1655 else
1656 {
1657 /* types disagree, so force typmod to -1 */
1658 colTypmods[colindex] = -1;
1659 }
1661 colindex++;
1662 }
1664 }
1665
1666 /*
1667 * Now we can build the tlist for the Append.
1668 */
1669 colindex = 0;
1672 {
1677
1678 Assert(reftle->resno == resno);
1679 Assert(!reftle->resjunk);
1680 expr = (Node *) makeVar(0,
1681 resno,
1682 colType,
1683 colTypmod,
1684 colColl,
1685 0);
1686 tle = makeTargetEntry((Expr *) expr,
1687 (AttrNumber) resno++,
1688 pstrdup(reftle->resname),
1689 false);
1690
1691 /*
1692 * By convention, all output columns in a setop tree have
1693 * ressortgroupref equal to their resno. In some cases the ref isn't
1694 * needed, but this is a cleaner way than modifying the tlist later.
1695 */
1696 tle->ressortgroupref = tle->resno;
1697
1698 tlist = lappend(tlist, tle);
1699 }
1700
1702
1703 return tlist;
1704}
int16 AttrNumber
Definition attnum.h:21
int32_t int32
Definition c.h:679
#define palloc_array(type, count)
Definition fe_memutils.h:91
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:1910
void pfree(void *pointer)
Definition mcxt.c:1619
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
int32 exprTypmod(const Node *expr)
Definition nodeFuncs.c:304
static int list_length(const List *l)
Definition pg_list.h:152
#define forthree(cell1, list1, cell2, list2, cell3, list3)
Definition pg_list.h:595
static ListCell * list_head(const List *l)
Definition pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition pg_list.h:375
#define lfirst_oid(lc)
Definition pg_list.h:174
unsigned int Oid
Definition nodes.h:133

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

1047{
1050 *rrel;
1051 double save_fraction = root->tuple_fraction;
1052 Path *lpath,
1053 *rpath,
1054 *path;
1056 *rpath_tlist,
1057 *tlist,
1058 *groupList;
1063 double dLeftGroups,
1065 dNumGroups,
1067 bool can_sort;
1068 bool can_hash;
1069 SetOpCmd cmd;
1070
1071 /*
1072 * Tell children to fetch all tuples.
1073 */
1074 root->tuple_fraction = 0.0;
1075
1076 /* Recurse on children */
1078 op,
1079 op->colTypes, op->colCollations,
1081 &lpath_tlist,
1083
1085 op,
1086 op->colTypes, op->colCollations,
1088 &rpath_tlist,
1090
1091 /*
1092 * Generate tlist for SetOp plan node.
1093 *
1094 * The tlist for a SetOp plan isn't important so far as the SetOp is
1095 * concerned, but we must make it look real anyway for the benefit of the
1096 * next plan level up.
1097 */
1098 tlist = generate_setop_tlist(op->colTypes, op->colCollations,
1099 0, false, lpath_tlist, refnames_tlist,
1101
1102 /* We should not have needed any type coercions in the tlist */
1104
1105 *pTargetList = tlist;
1106
1107 /* Identify the grouping semantics */
1108 groupList = generate_setop_grouplist(op, tlist);
1109
1110 /* Check whether the operators support sorting or hashing */
1111 can_sort = grouping_is_sortable(groupList);
1112 can_hash = grouping_is_hashable(groupList);
1113 if (!can_sort && !can_hash)
1114 ereport(ERROR,
1116 /* translator: %s is INTERSECT or EXCEPT */
1117 errmsg("could not implement %s",
1118 (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT"),
1119 errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
1120
1121 if (can_sort)
1122 {
1123 /* Determine the pathkeys for sorting by the whole target list */
1125 tlist);
1126
1127 root->query_pathkeys = nonunion_pathkeys;
1128 }
1129
1130 /*
1131 * Now that we've got all that info, we can build the child paths.
1132 */
1133 if (lrel->rtekind == RTE_SUBQUERY)
1136 else
1137 dLeftGroups = lrel->rows;
1138 if (rrel->rtekind == RTE_SUBQUERY)
1141 else
1142 dRightGroups = rrel->rows;
1143
1144 /* Undo effects of forcing tuple_fraction to 0 */
1145 root->tuple_fraction = save_fraction;
1146
1147 /*
1148 * For EXCEPT, we must put the left input first. For INTERSECT, either
1149 * order should give the same results, and we prefer to put the smaller
1150 * input first in order to (a) minimize the size of the hash table in the
1151 * hashing case, and (b) improve our chances of exploiting the executor's
1152 * fast path for empty left-hand input. "Smaller" means the one with the
1153 * fewer groups.
1154 */
1155 if (op->op != SETOP_EXCEPT && dLeftGroups > dRightGroups)
1156 {
1157 /* need to swap the two inputs */
1159 List *tmplist;
1160 double tmpd;
1161
1162 tmprel = lrel;
1163 lrel = rrel;
1164 rrel = tmprel;
1168 tmpd = dLeftGroups;
1171 }
1172
1173 lpath = lrel->cheapest_total_path;
1174 rpath = rrel->cheapest_total_path;
1175
1176 /* Build result relation. */
1178 bms_union(lrel->relids, rrel->relids));
1179
1180 /*
1181 * Create the PathTarget and set the width accordingly. For EXCEPT, since
1182 * the set op result won't contain rows from the rpath, we only account
1183 * for the width of the lpath. For INTERSECT, use both input paths.
1184 */
1185 if (op->op == SETOP_EXCEPT)
1186 result_rel->reltarget = create_setop_pathtarget(root, tlist,
1187 list_make1(lpath));
1188 else
1189 result_rel->reltarget = create_setop_pathtarget(root, tlist,
1190 list_make2(lpath, rpath));
1191
1192 /* Check for provably empty setop inputs and add short-circuit paths. */
1193 if (op->op == SETOP_EXCEPT)
1194 {
1195 /*
1196 * For EXCEPTs, if the left side is dummy then there's no need to
1197 * inspect the right-hand side as scanning the right to find tuples to
1198 * remove won't make the left-hand input any more empty.
1199 */
1200 if (is_dummy_rel(lrel))
1201 {
1203
1204 return result_rel;
1205 }
1206
1207 /* Handle EXCEPTs with dummy right input */
1208 if (is_dummy_rel(rrel))
1209 {
1210 if (op->all)
1211 {
1212 Path *apath;
1213 AppendPathInput append = {0};
1214
1216
1217 /*
1218 * EXCEPT ALL: If the right-hand input is dummy then we can
1219 * simply scan the left-hand input. To keep createplan.c
1220 * happy, use a single child Append to handle the translation
1221 * between the set op targetlist and the targetlist of the
1222 * left input. The Append will be removed in setrefs.c.
1223 */
1225 append, NIL, NULL, 0,
1226 false, -1);
1227
1229
1230 return result_rel;
1231 }
1232 else
1233 {
1234 /*
1235 * To make EXCEPT with a dummy RHS work means having to
1236 * deduplicate the left input. That could be done with
1237 * AggPaths, but it doesn't seem worth the effort. Let the
1238 * normal path generation code below handle this one.
1239 */
1240 }
1241 }
1242 }
1243 else
1244 {
1245 /*
1246 * For INTERSECT, if either input is a dummy rel then we can mark the
1247 * result_rel as dummy since intersecting with an empty relation can
1248 * never yield any results. This is true regardless of INTERSECT or
1249 * INTERSECT ALL.
1250 */
1252 {
1254
1255 return result_rel;
1256 }
1257 }
1258
1259 /*
1260 * Estimate number of distinct groups that we'll need hashtable entries
1261 * for; this is the size of the left-hand input for EXCEPT, or the smaller
1262 * input for INTERSECT. Also estimate the number of eventual output rows.
1263 * In non-ALL cases, we estimate each group produces one output row; in
1264 * ALL cases use the relevant relation size. These are worst-case
1265 * estimates, of course, but we need to be conservative.
1266 */
1267 if (op->op == SETOP_EXCEPT)
1268 {
1270 dNumOutputRows = op->all ? lpath->rows : dNumGroups;
1271 }
1272 else
1273 {
1275 dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
1276 }
1277 result_rel->rows = dNumOutputRows;
1278
1279 /* Select the SetOpCmd type */
1280 switch (op->op)
1281 {
1282 case SETOP_INTERSECT:
1284 break;
1285 case SETOP_EXCEPT:
1287 break;
1288 default:
1289 elog(ERROR, "unrecognized set op: %d", (int) op->op);
1290 cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */
1291 break;
1292 }
1293
1294 /*
1295 * If we can hash, that just requires a SetOp atop the cheapest inputs.
1296 */
1297 if (can_hash)
1298 {
1299 path = (Path *) create_setop_path(root,
1300 result_rel,
1301 lpath,
1302 rpath,
1303 cmd,
1305 groupList,
1306 dNumGroups,
1308 add_path(result_rel, path);
1309 }
1310
1311 /*
1312 * If we can sort, generate the cheapest sorted input paths, and add a
1313 * SetOp atop those.
1314 */
1315 if (can_sort)
1316 {
1317 List *pathkeys;
1318 Path *slpath,
1319 *srpath;
1320
1321 /* First the left input ... */
1323 groupList,
1324 lpath_tlist);
1325 if (pathkeys_contained_in(pathkeys, lpath->pathkeys))
1326 slpath = lpath; /* cheapest path is already sorted */
1327 else
1328 {
1331 NULL,
1332 TOTAL_COST,
1333 false);
1334 /* Subquery failed to produce any presorted paths? */
1335 if (slpath == NULL)
1337 lpath->parent,
1338 lpath,
1339 pathkeys,
1340 -1.0);
1341 }
1342
1343 /* and now the same for the right. */
1345 groupList,
1346 rpath_tlist);
1347 if (pathkeys_contained_in(pathkeys, rpath->pathkeys))
1348 srpath = rpath; /* cheapest path is already sorted */
1349 else
1350 {
1353 NULL,
1354 TOTAL_COST,
1355 false);
1356 /* Subquery failed to produce any presorted paths? */
1357 if (srpath == NULL)
1359 rpath->parent,
1360 rpath,
1361 pathkeys,
1362 -1.0);
1363 }
1364
1365 path = (Path *) create_setop_path(root,
1366 result_rel,
1367 slpath,
1368 srpath,
1369 cmd,
1371 groupList,
1372 dNumGroups,
1374 add_path(result_rel, path);
1375 }
1376
1377 return result_rel;
1378}
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:252
#define Min(x, y)
Definition c.h:1131
int errcode(int sqlerrcode)
Definition elog.c:875
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
SetOpCmd
Definition nodes.h:405
@ SETOPCMD_EXCEPT
Definition nodes.h:408
@ SETOPCMD_EXCEPT_ALL
Definition nodes.h:409
@ SETOPCMD_INTERSECT_ALL
Definition nodes.h:407
@ SETOPCMD_INTERSECT
Definition nodes.h:406
@ SETOP_HASHED
Definition nodes.h:415
@ SETOP_SORTED
Definition nodes.h:414
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:3476
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:244
#define list_make2(x1, x2)
Definition pg_list.h:246
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition prepunion.c:1718
static PathTarget * create_setop_pathtarget(PlannerInfo *root, List *tlist, List *child_pathlist)
Definition prepunion.c:1758
static List * generate_setop_tlist(List *colTypes, List *colCollations, Index varno, bool hack_constants, List *input_tlist, List *refnames_tlist, bool *trivial_tlist)
Definition prepunion.c:1481
static RelOptInfo * recurse_set_operations(Node *setOp, PlannerInfo *root, SetOperationStmt *parentOp, List *colTypes, List *colCollations, List *refnames_tlist, List **pTargetList, bool *istrivial_tlist)
Definition prepunion.c:214
static 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:2011
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:3605
static List * generate_append_tlist(List *colTypes, List *colCollations, List *input_tlists, List *refnames_tlist)
Definition prepunion.c:1609

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

1719{
1720 List *grouplist = copyObject(op->groupClauses);
1721 ListCell *lg;
1722 ListCell *lt;
1723
1725 foreach(lt, targetlist)
1726 {
1727 TargetEntry *tle = (TargetEntry *) lfirst(lt);
1729
1730 Assert(!tle->resjunk);
1731
1732 /* non-resjunk columns should have sortgroupref = resno */
1733 Assert(tle->ressortgroupref == tle->resno);
1734
1735 /* non-resjunk columns should have grouping clauses */
1736 Assert(lg != NULL);
1738 lg = lnext(grouplist, lg);
1739 Assert(sgc->tleSortGroupRef == 0);
1740
1741 sgc->tleSortGroupRef = tle->ressortgroupref;
1742 }
1743 Assert(lg == NULL);
1744 return grouplist;
1745}
#define copyObject(obj)
Definition nodes.h:230

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

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

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 double dNumChildGroups = 0;
715
716 /*
717 * If any of my children are identical UNION nodes (same op, all-flag, and
718 * colTypes/colCollations) then they can be merged into this node so that
719 * we generate only one Append/MergeAppend and unique-ification for the
720 * lot. Recurse to find such nodes.
721 */
723 op,
725 &tlist_list,
727
728 /*
729 * Generate tlist for Append/MergeAppend plan node.
730 *
731 * The tlist for an Append plan isn't important as far as the Append is
732 * concerned, but we must make it look real anyway for the benefit of the
733 * next plan level up.
734 */
735 tlist = generate_append_tlist(op->colTypes, op->colCollations,
737 *pTargetList = tlist;
738
739 /* For UNIONs (not UNION ALL), try sorting, if sorting is possible */
740 if (!op->all)
741 {
742 /* Identify the grouping semantics */
743 groupList = generate_setop_grouplist(op, tlist);
744
745 if (grouping_is_sortable(op->groupClauses))
746 {
747 try_sorted = true;
748 /* Determine the pathkeys for sorting by the whole target list */
750 tlist);
751
752 root->query_pathkeys = union_pathkeys;
753 }
754 }
755
756 /*
757 * Now that we've got the append target list, we can build the union child
758 * paths.
759 */
761 {
762 RelOptInfo *rel = lfirst(lc);
765 double childGroups = 0;
766
767 /* only build paths for the union children */
768 if (rel->rtekind == RTE_SUBQUERY)
771 op->all ? NULL : &childGroups);
772 else
773 childGroups = rel->rows;
774
775 /*
776 * For UNION (not UNION ALL), accumulate the per-child distinct-group
777 * estimates. This sum is the basis for the UNION's output estimate
778 * below: since distinct(A union B) <= distinct(A) + distinct(B), the
779 * union cannot have more distinct rows than its children do in total.
780 * Children that are known to be empty contribute nothing, so skip
781 * them.
782 */
783 if (!op->all && !is_dummy_rel(rel))
785 }
786
787 /* Build path lists and relid set. */
788 foreach(lc, rellist)
789 {
790 RelOptInfo *rel = lfirst(lc);
792
793 /*
794 * Record the relids so that we can identify the correct
795 * UPPERREL_SETOP RelOptInfo below.
796 */
797 relids = bms_add_members(relids, rel->relids);
798
799 /* Skip any UNION children that are proven not to yield any rows */
800 if (is_dummy_rel(rel))
801 continue;
802
803 cheapest.subpaths = lappend(cheapest.subpaths,
805
806 if (try_sorted)
807 {
810 NULL,
812 false);
813
814 if (ordered_path != NULL)
815 ordered.subpaths = lappend(ordered.subpaths, ordered_path);
816 else
817 {
818 /*
819 * If we can't find a sorted path, just give up trying to
820 * generate a list of correctly sorted child paths. This can
821 * happen when type coercion was added to the targetlist due
822 * to mismatching types from the union children.
823 */
824 try_sorted = false;
825 }
826 }
827
828 if (consider_parallel)
829 {
830 if (!rel->consider_parallel)
831 {
832 consider_parallel = false;
833 partial_paths_valid = false;
834 }
835 else if (rel->partial_pathlist == NIL)
836 partial_paths_valid = false;
837 else
838 partial.partial_subpaths = lappend(partial.partial_subpaths,
840 }
841 }
842
843 /* Build result relation. */
845 result_rel->reltarget = create_setop_pathtarget(root, tlist,
846 cheapest.subpaths);
847 result_rel->consider_parallel = consider_parallel;
848 result_rel->consider_startup = (root->tuple_fraction > 0);
849
850 /* If all UNION children were dummy rels, make the resulting rel dummy */
851 if (cheapest.subpaths == NIL)
852 {
854
855 return result_rel;
856 }
857
858 /*
859 * Append the child results together using the cheapest paths from each
860 * union child.
861 */
863 NIL, NULL, 0, false, -1);
864
865 /*
866 * Initialize the result row estimate to the total input size. This is
867 * correct for UNION ALL; for the UNION case it is overwritten below with
868 * the estimated number of distinct groups.
869 */
870 result_rel->rows = apath->rows;
871
872 /*
873 * Now consider doing the same thing using the partial paths plus Append
874 * plus Gather.
875 */
877 {
878 Path *papath;
879 int parallel_workers = 0;
880
881 /* Find the highest number of workers requested for any subpath. */
882 foreach(lc, partial.partial_subpaths)
883 {
884 Path *subpath = lfirst(lc);
885
886 parallel_workers = Max(parallel_workers,
887 subpath->parallel_workers);
888 }
889 Assert(parallel_workers > 0);
890
891 /*
892 * If the use of parallel append is permitted, always request at least
893 * log2(# of children) paths. We assume it can be useful to have
894 * extra workers in this case because they will be spread out across
895 * the children. The precise formula is just a guess; see
896 * add_paths_to_append_rel.
897 */
899 {
900 parallel_workers = Max(parallel_workers,
902 parallel_workers = Min(parallel_workers,
904 }
905 Assert(parallel_workers > 0);
906
907 papath = (Path *)
909 NIL, NULL, parallel_workers,
911 gpath = (Path *)
913 result_rel->reltarget, NULL, NULL);
914 }
915
916 if (!op->all)
917 {
918 bool can_sort = grouping_is_sortable(groupList);
919 bool can_hash = grouping_is_hashable(groupList);
920
921 /*
922 * result_rel->rows was initialized to the total input size above,
923 * which is the correct estimate for UNION ALL. A UNION removes
924 * duplicates, so override it with the estimated number of distinct
925 * groups.
926 */
928
929 if (can_hash)
930 {
931 Path *path;
932
933 /*
934 * Try a hash aggregate plan on 'apath'. This is the cheapest
935 * available path containing each append child.
936 */
937 path = (Path *) create_agg_path(root,
939 apath,
940 result_rel->reltarget,
943 groupList,
944 NIL,
945 NULL,
947 add_path(result_rel, path);
948
949 /* Try hash aggregate on the Gather path, if valid */
950 if (gpath != NULL)
951 {
952 /* Hashed aggregate plan --- no sort needed */
953 path = (Path *) create_agg_path(root,
955 gpath,
956 result_rel->reltarget,
959 groupList,
960 NIL,
961 NULL,
963 add_path(result_rel, path);
964 }
965 }
966
967 if (can_sort)
968 {
969 Path *path = apath;
970
971 /* Try Sort -> Unique on the Append path */
972 if (groupList != NIL)
973 path = (Path *) create_sort_path(root, result_rel, path,
974 make_pathkeys_for_sortclauses(root, groupList, tlist),
975 -1.0);
976
977 path = (Path *) create_unique_path(root,
979 path,
980 list_length(path->pathkeys),
982
983 add_path(result_rel, path);
984
985 /* Try Sort -> Unique on the Gather path, if set */
986 if (gpath != NULL)
987 {
988 path = gpath;
989
990 path = (Path *) create_sort_path(root, result_rel, path,
991 make_pathkeys_for_sortclauses(root, groupList, tlist),
992 -1.0);
993
994 path = (Path *) create_unique_path(root,
996 path,
997 list_length(path->pathkeys),
999 add_path(result_rel, path);
1000 }
1001 }
1002
1003 /*
1004 * Try making a MergeAppend path if we managed to find a path with the
1005 * correct pathkeys in each union child query.
1006 */
1007 if (try_sorted && groupList != NIL)
1008 {
1009 Path *path;
1010
1012 result_rel,
1013 ordered.subpaths,
1014 NIL,
1016 NULL);
1017
1018 /* and make the MergeAppend unique */
1019 path = (Path *) create_unique_path(root,
1020 result_rel,
1021 path,
1022 list_length(tlist),
1024
1025 add_path(result_rel, path);
1026 }
1027 }
1028 else
1029 {
1030 /* UNION ALL */
1032
1033 if (gpath != NULL)
1035 }
1036
1037 return result_rel;
1038}
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:1036
#define Max(x, y)
Definition c.h:1125
int max_parallel_workers_per_gather
Definition costsize.c:144
bool enable_parallel_append
Definition costsize.c:163
@ AGG_HASHED
Definition nodes.h:364
@ AGGSPLIT_SIMPLE
Definition nodes.h:385
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:3067
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:1393
List * partial_subpaths
Definition pathnode.h:37
Relids relids
Definition pathnodes.h:1021
List * pathlist
Definition pathnodes.h:1050
List * partial_pathlist
Definition pathnodes.h:1052
Cardinality rows
Definition pathnodes.h:1027

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, 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, root, RelOptInfo::rows, 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:180
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 1393 of file prepunion.c.

1398{
1400 List *result = NIL;
1402 bool trivial_tlist;
1403
1404 *tlist_list = NIL;
1406
1407 while (pending_rels != NIL)
1408 {
1410
1412
1414 {
1416
1417 if (op->op == top_union->op &&
1418 (op->all == top_union->all || op->all) &&
1419 equal(op->colTypes, top_union->colTypes) &&
1420 equal(op->colCollations, top_union->colCollations))
1421 {
1422 /* Same UNION, so fold children into parent */
1425 continue;
1426 }
1427 }
1428
1429 /*
1430 * Not same, so plan this child separately.
1431 *
1432 * If top_union isn't a UNION ALL, then we are interested in sorted
1433 * output from the child, so pass top_union as parentOp. Note that
1434 * this isn't necessarily the child node's immediate SetOperationStmt
1435 * parent, but that's fine: it's the effective parent.
1436 */
1438 top_union->all ? NULL : top_union,
1442 &child_tlist,
1443 &trivial_tlist));
1446 }
1447
1448 return result;
1449}
uint32 result
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(), result, and root.

Referenced by generate_union_paths().

◆ postprocess_setop_rel()

static void postprocess_setop_rel ( PlannerInfo root,
RelOptInfo rel 
)
static

Definition at line 1455 of file prepunion.c.

1456{
1457 /*
1458 * We don't currently worry about allowing FDWs to contribute paths to
1459 * this relation, but give extensions a chance.
1460 */
1462 (*create_upper_paths_hook) (root, UPPERREL_SETOP,
1463 NULL, rel, NULL);
1464
1465 /* Select cheapest path */
1466 set_cheapest(rel);
1467}
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, NULL,
254 false, root->tuple_fraction,
255 parentOp);
256
257 /*
258 * It should not be possible for the primitive query to contain any
259 * cross-references to other primitive queries in the setop tree.
260 */
261 if (root->plan_params)
262 elog(ERROR, "unexpected outer reference in set operation subquery");
263
264 /* Figure out the appropriate target list for this subquery. */
266 rtr->rtindex,
267 true,
268 subroot->processed_tlist,
271 rel->reltarget = create_pathtarget(root, tlist);
272
273 /* Return the fully-fledged tlist to caller, too */
274 *pTargetList = tlist;
276 }
277 else if (IsA(setOp, SetOperationStmt))
278 {
280
281 /* UNIONs are much different from INTERSECT/EXCEPT */
282 if (op->op == SETOP_UNION)
283 rel = generate_union_paths(op, root,
286 else
290
291 /*
292 * If necessary, add a Result node to project the caller-requested
293 * output columns.
294 *
295 * XXX you don't really want to know about this: setrefs.c will apply
296 * fix_upper_expr() to the Result node's tlist. This would fail if the
297 * Vars generated by generate_setop_tlist() were not exactly equal()
298 * to the corresponding tlist entries of the subplan. However, since
299 * the subplan was generated by generate_union_paths() or
300 * generate_nonunion_paths(), and hence its tlist was generated by
301 * generate_append_tlist() or generate_setop_tlist(), this will work.
302 * We just tell generate_setop_tlist() to use varno 0.
303 */
306 {
307 PathTarget *target;
308 bool trivial_tlist;
309 ListCell *lc;
310
312 0,
313 false,
319
320 /* Apply projection to each path */
321 foreach(lc, rel->pathlist)
322 {
323 Path *subpath = (Path *) lfirst(lc);
324 Path *path;
325
326 Assert(subpath->param_info == NULL);
327 path = apply_projection_to_path(root, subpath->parent,
328 subpath, target);
329 /* If we had to add a Result, path is different from subpath */
330 if (path != subpath)
331 lfirst(lc) = path;
332 }
333
334 /* Apply projection to each partial path */
335 foreach(lc, rel->partial_pathlist)
336 {
337 Path *subpath = (Path *) lfirst(lc);
338 Path *path;
339
340 Assert(subpath->param_info == NULL);
341
342 /* avoid apply_projection_to_path, in case of multiple refs */
343 path = (Path *) create_projection_path(root, subpath->parent,
344 subpath, target);
345 lfirst(lc) = path;
346 }
347 }
349 }
350 else
351 {
352 elog(ERROR, "unrecognized node type: %d",
353 (int) nodeTag(setOp));
354 *pTargetList = NIL;
355 rel = NULL; /* keep compiler quiet */
356 }
357
358 return rel;
359}
#define nodeTag(nodeptr)
Definition nodes.h:137
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
PlannerInfo * subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name, PlannerInfo *parent_root, PlannerInfo *alternative_root, bool hasRecursion, double tuple_fraction, SetOperationStmt *setops)
Definition planner.c:770
char * choose_plan_name(PlannerGlobal *glob, const char *name, bool always_number)
Definition planner.c:9215
static RelOptInfo * generate_union_paths(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition prepunion.c:691
static RelOptInfo * generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition prepunion.c:1044
RelOptInfo * build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
Definition relnode.c:212
void check_stack_depth(void)
Definition stack_depth.c:96
List * processed_tlist
Definition pathnodes.h:593
struct PathTarget * reltarget
Definition pathnodes.h:1045
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().