PostgreSQL Source Code  git master
prepunion.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "catalog/partition.h"
#include "catalog/pg_inherits.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/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/tlist.h"
#include "parser/parse_coerce.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
#include "utils/syscache.h"
Include dependency graph for prepunion.c:

Go to the source code of this file.

Functions

static RelOptInforecurse_set_operations (Node *setOp, PlannerInfo *root, List *colTypes, List *colCollations, bool junkOK, int flag, List *refnames_tlist, List **pTargetList, double *pNumGroups)
 
static RelOptInfogenerate_recursion_path (SetOperationStmt *setOp, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
 
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)
 
static Pathmake_union_unique (SetOperationStmt *op, Path *path, List *tlist, PlannerInfo *root)
 
static void postprocess_setop_rel (PlannerInfo *root, RelOptInfo *rel)
 
static bool choose_hashed_setop (PlannerInfo *root, List *groupClauses, Path *input_path, double dNumGroups, double dNumOutputRows, const char *construct)
 
static Listgenerate_setop_tlist (List *colTypes, List *colCollations, int flag, Index varno, bool hack_constants, List *input_tlist, List *refnames_tlist, bool *trivial_tlist)
 
static Listgenerate_append_tlist (List *colTypes, List *colCollations, bool flag, List *input_tlists, List *refnames_tlist)
 
static Listgenerate_setop_grouplist (SetOperationStmt *op, List *targetlist)
 
RelOptInfoplan_set_operations (PlannerInfo *root)
 

Function Documentation

◆ choose_hashed_setop()

static bool choose_hashed_setop ( PlannerInfo root,
List groupClauses,
Path input_path,
double  dNumGroups,
double  dNumOutputRows,
const char *  construct 
)
static

Definition at line 1026 of file prepunion.c.

1030 {
1031  int numGroupCols = list_length(groupClauses);
1032  Size hash_mem_limit = get_hash_memory_limit();
1033  bool can_sort;
1034  bool can_hash;
1035  Size hashentrysize;
1036  Path hashed_p;
1037  Path sorted_p;
1038  double tuple_fraction;
1039 
1040  /* Check whether the operators support sorting or hashing */
1041  can_sort = grouping_is_sortable(groupClauses);
1042  can_hash = grouping_is_hashable(groupClauses);
1043  if (can_hash && can_sort)
1044  {
1045  /* we have a meaningful choice to make, continue ... */
1046  }
1047  else if (can_hash)
1048  return true;
1049  else if (can_sort)
1050  return false;
1051  else
1052  ereport(ERROR,
1053  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1054  /* translator: %s is UNION, INTERSECT, or EXCEPT */
1055  errmsg("could not implement %s", construct),
1056  errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
1057 
1058  /* Prefer sorting when enable_hashagg is off */
1059  if (!enable_hashagg)
1060  return false;
1061 
1062  /*
1063  * Don't do it if it doesn't look like the hashtable will fit into
1064  * hash_mem.
1065  */
1066  hashentrysize = MAXALIGN(input_path->pathtarget->width) + MAXALIGN(SizeofMinimalTupleHeader);
1067 
1068  if (hashentrysize * dNumGroups > hash_mem_limit)
1069  return false;
1070 
1071  /*
1072  * See if the estimated cost is no more than doing it the other way.
1073  *
1074  * We need to consider input_plan + hashagg versus input_plan + sort +
1075  * group. Note that the actual result plan might involve a SetOp or
1076  * Unique node, not Agg or Group, but the cost estimates for Agg and Group
1077  * should be close enough for our purposes here.
1078  *
1079  * These path variables are dummies that just hold cost fields; we don't
1080  * make actual Paths for these steps.
1081  */
1082  cost_agg(&hashed_p, root, AGG_HASHED, NULL,
1083  numGroupCols, dNumGroups,
1084  NIL,
1085  input_path->startup_cost, input_path->total_cost,
1086  input_path->rows, input_path->pathtarget->width);
1087 
1088  /*
1089  * Now for the sorted case. Note that the input is *always* unsorted,
1090  * since it was made by appending unrelated sub-relations together.
1091  */
1092  sorted_p.startup_cost = input_path->startup_cost;
1093  sorted_p.total_cost = input_path->total_cost;
1094  /* XXX cost_sort doesn't actually look at pathkeys, so just pass NIL */
1095  cost_sort(&sorted_p, root, NIL, sorted_p.total_cost,
1096  input_path->rows, input_path->pathtarget->width,
1097  0.0, work_mem, -1.0);
1098  cost_group(&sorted_p, root, numGroupCols, dNumGroups,
1099  NIL,
1100  sorted_p.startup_cost, sorted_p.total_cost,
1101  input_path->rows);
1102 
1103  /*
1104  * Now make the decision using the top-level tuple fraction. First we
1105  * have to convert an absolute count (LIMIT) into fractional form.
1106  */
1107  tuple_fraction = root->tuple_fraction;
1108  if (tuple_fraction >= 1.0)
1109  tuple_fraction /= dNumOutputRows;
1110 
1111  if (compare_fractional_path_costs(&hashed_p, &sorted_p,
1112  tuple_fraction) < 0)
1113  {
1114  /* Hashed is cheaper, so use it */
1115  return true;
1116  }
1117  return false;
1118 }
#define MAXALIGN(LEN)
Definition: c.h:800
size_t Size
Definition: c.h:594
void cost_agg(Path *path, PlannerInfo *root, AggStrategy aggstrategy, const AggClauseCosts *aggcosts, int numGroupCols, double numGroups, List *quals, Cost input_startup_cost, Cost input_total_cost, double input_tuples, double input_width)
Definition: costsize.c:2651
bool enable_hashagg
Definition: costsize.c:142
void cost_sort(Path *path, PlannerInfo *root, List *pathkeys, Cost input_cost, double tuples, int width, Cost comparison_cost, int sort_mem, double limit_tuples)
Definition: costsize.c:2125
void cost_group(Path *path, PlannerInfo *root, int numGroupCols, double numGroups, List *quals, Cost input_startup_cost, Cost input_total_cost, double input_tuples)
Definition: costsize.c:3164
int errdetail(const char *fmt,...)
Definition: elog.c:1208
int errcode(int sqlerrcode)
Definition: elog.c:860
int errmsg(const char *fmt,...)
Definition: elog.c:1075
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
int work_mem
Definition: globals.c:128
#define SizeofMinimalTupleHeader
Definition: htup_details.h:647
size_t get_hash_memory_limit(void)
Definition: nodeHash.c:3596
@ AGG_HASHED
Definition: nodes.h:345
int compare_fractional_path_costs(Path *path1, Path *path2, double fraction)
Definition: pathnode.c:117
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
Cardinality rows
Definition: pathnodes.h:1640
Cost startup_cost
Definition: pathnodes.h:1641
Cost total_cost
Definition: pathnodes.h:1642
Selectivity tuple_fraction
Definition: pathnodes.h:478
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:540
bool grouping_is_hashable(List *groupClause)
Definition: tlist.c:560

References AGG_HASHED, compare_fractional_path_costs(), cost_agg(), cost_group(), cost_sort(), enable_hashagg, ereport, errcode(), errdetail(), errmsg(), ERROR, get_hash_memory_limit(), grouping_is_hashable(), grouping_is_sortable(), list_length(), MAXALIGN, NIL, Path::rows, SizeofMinimalTupleHeader, Path::startup_cost, Path::total_cost, PlannerInfo::tuple_fraction, and work_mem.

Referenced by generate_nonunion_paths(), and make_union_unique().

◆ generate_append_tlist()

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

Definition at line 1282 of file prepunion.c.

1286 {
1287  List *tlist = NIL;
1288  int resno = 1;
1289  ListCell *curColType;
1290  ListCell *curColCollation;
1291  ListCell *ref_tl_item;
1292  int colindex;
1293  TargetEntry *tle;
1294  Node *expr;
1295  ListCell *tlistl;
1296  int32 *colTypmods;
1297 
1298  /*
1299  * First extract typmods to use.
1300  *
1301  * If the inputs all agree on type and typmod of a particular column, use
1302  * that typmod; else use -1.
1303  */
1304  colTypmods = (int32 *) palloc(list_length(colTypes) * sizeof(int32));
1305 
1306  foreach(tlistl, input_tlists)
1307  {
1308  List *subtlist = (List *) lfirst(tlistl);
1309  ListCell *subtlistl;
1310 
1311  curColType = list_head(colTypes);
1312  colindex = 0;
1313  foreach(subtlistl, subtlist)
1314  {
1315  TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
1316 
1317  if (subtle->resjunk)
1318  continue;
1319  Assert(curColType != NULL);
1320  if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1321  {
1322  /* If first subplan, copy the typmod; else compare */
1323  int32 subtypmod = exprTypmod((Node *) subtle->expr);
1324 
1325  if (tlistl == list_head(input_tlists))
1326  colTypmods[colindex] = subtypmod;
1327  else if (subtypmod != colTypmods[colindex])
1328  colTypmods[colindex] = -1;
1329  }
1330  else
1331  {
1332  /* types disagree, so force typmod to -1 */
1333  colTypmods[colindex] = -1;
1334  }
1335  curColType = lnext(colTypes, curColType);
1336  colindex++;
1337  }
1338  Assert(curColType == NULL);
1339  }
1340 
1341  /*
1342  * Now we can build the tlist for the Append.
1343  */
1344  colindex = 0;
1345  forthree(curColType, colTypes, curColCollation, colCollations,
1346  ref_tl_item, refnames_tlist)
1347  {
1348  Oid colType = lfirst_oid(curColType);
1349  int32 colTypmod = colTypmods[colindex++];
1350  Oid colColl = lfirst_oid(curColCollation);
1351  TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
1352 
1353  Assert(reftle->resno == resno);
1354  Assert(!reftle->resjunk);
1355  expr = (Node *) makeVar(0,
1356  resno,
1357  colType,
1358  colTypmod,
1359  colColl,
1360  0);
1361  tle = makeTargetEntry((Expr *) expr,
1362  (AttrNumber) resno++,
1363  pstrdup(reftle->resname),
1364  false);
1365 
1366  /*
1367  * By convention, all non-resjunk columns in a setop tree have
1368  * ressortgroupref equal to their resno. In some cases the ref isn't
1369  * needed, but this is a cleaner way than modifying the tlist later.
1370  */
1371  tle->ressortgroupref = tle->resno;
1372 
1373  tlist = lappend(tlist, tle);
1374  }
1375 
1376  if (flag)
1377  {
1378  /* Add a resjunk flag column */
1379  /* flag value is shown as copied up from subplan */
1380  expr = (Node *) makeVar(0,
1381  resno,
1382  INT4OID,
1383  -1,
1384  InvalidOid,
1385  0);
1386  tle = makeTargetEntry((Expr *) expr,
1387  (AttrNumber) resno++,
1388  pstrdup("flag"),
1389  true);
1390  tlist = lappend(tlist, tle);
1391  }
1392 
1393  pfree(colTypmods);
1394 
1395  return tlist;
1396 }
int16 AttrNumber
Definition: attnum.h:21
signed int int32
Definition: c.h:483
Assert(fmt[strlen(fmt) - 1] !='\n')
List * lappend(List *list, void *datum)
Definition: list.c:339
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:241
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
char * pstrdup(const char *in)
Definition: mcxt.c:1619
void pfree(void *pointer)
Definition: mcxt.c:1431
void * palloc(Size size)
Definition: mcxt.c:1201
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:43
int32 exprTypmod(const Node *expr)
Definition: nodeFuncs.c:282
#define lfirst(lc)
Definition: pg_list.h:172
#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
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
Definition: pg_list.h:54
Definition: nodes.h:129
Expr * expr
Definition: primnodes.h:1922
AttrNumber resno
Definition: primnodes.h:1924
Index ressortgroupref
Definition: primnodes.h:1928
char * flag(int b)
Definition: test-ctype.c:33

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

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

713 {
714  RelOptInfo *result_rel;
715  RelOptInfo *lrel,
716  *rrel;
717  double save_fraction = root->tuple_fraction;
718  Path *lpath,
719  *rpath,
720  *path;
721  List *lpath_tlist,
722  *rpath_tlist,
723  *tlist_list,
724  *tlist,
725  *groupList,
726  *pathlist;
727  double dLeftGroups,
728  dRightGroups,
729  dNumGroups,
730  dNumOutputRows;
731  bool use_hash;
732  SetOpCmd cmd;
733  int firstFlag;
734 
735  /*
736  * Tell children to fetch all tuples.
737  */
738  root->tuple_fraction = 0.0;
739 
740  /* Recurse on children, ensuring their outputs are marked */
741  lrel = recurse_set_operations(op->larg, root,
742  op->colTypes, op->colCollations,
743  false, 0,
744  refnames_tlist,
745  &lpath_tlist,
746  &dLeftGroups);
747  lpath = lrel->cheapest_total_path;
748  rrel = recurse_set_operations(op->rarg, root,
749  op->colTypes, op->colCollations,
750  false, 1,
751  refnames_tlist,
752  &rpath_tlist,
753  &dRightGroups);
754  rpath = rrel->cheapest_total_path;
755 
756  /* Undo effects of forcing tuple_fraction to 0 */
757  root->tuple_fraction = save_fraction;
758 
759  /*
760  * For EXCEPT, we must put the left input first. For INTERSECT, either
761  * order should give the same results, and we prefer to put the smaller
762  * input first in order to minimize the size of the hash table in the
763  * hashing case. "Smaller" means the one with the fewer groups.
764  */
765  if (op->op == SETOP_EXCEPT || dLeftGroups <= dRightGroups)
766  {
767  pathlist = list_make2(lpath, rpath);
768  tlist_list = list_make2(lpath_tlist, rpath_tlist);
769  firstFlag = 0;
770  }
771  else
772  {
773  pathlist = list_make2(rpath, lpath);
774  tlist_list = list_make2(rpath_tlist, lpath_tlist);
775  firstFlag = 1;
776  }
777 
778  /*
779  * Generate tlist for Append plan node.
780  *
781  * The tlist for an Append plan isn't important as far as the Append is
782  * concerned, but we must make it look real anyway for the benefit of the
783  * next plan level up. In fact, it has to be real enough that the flag
784  * column is shown as a variable not a constant, else setrefs.c will get
785  * confused.
786  */
787  tlist = generate_append_tlist(op->colTypes, op->colCollations, true,
788  tlist_list, refnames_tlist);
789 
790  *pTargetList = tlist;
791 
792  /* Build result relation. */
793  result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
794  bms_union(lrel->relids, rrel->relids));
795  result_rel->reltarget = create_pathtarget(root, tlist);
796 
797  /*
798  * Append the child results together.
799  */
800  path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
801  NIL, NULL, 0, false, -1);
802 
803  /* Identify the grouping semantics */
804  groupList = generate_setop_grouplist(op, tlist);
805 
806  /*
807  * Estimate number of distinct groups that we'll need hashtable entries
808  * for; this is the size of the left-hand input for EXCEPT, or the smaller
809  * input for INTERSECT. Also estimate the number of eventual output rows.
810  * In non-ALL cases, we estimate each group produces one output row; in
811  * ALL cases use the relevant relation size. These are worst-case
812  * estimates, of course, but we need to be conservative.
813  */
814  if (op->op == SETOP_EXCEPT)
815  {
816  dNumGroups = dLeftGroups;
817  dNumOutputRows = op->all ? lpath->rows : dNumGroups;
818  }
819  else
820  {
821  dNumGroups = Min(dLeftGroups, dRightGroups);
822  dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
823  }
824 
825  /*
826  * Decide whether to hash or sort, and add a sort node if needed.
827  */
828  use_hash = choose_hashed_setop(root, groupList, path,
829  dNumGroups, dNumOutputRows,
830  (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT");
831 
832  if (groupList && !use_hash)
833  path = (Path *) create_sort_path(root,
834  result_rel,
835  path,
837  groupList,
838  tlist),
839  -1.0);
840 
841  /*
842  * Finally, add a SetOp path node to generate the correct output.
843  */
844  switch (op->op)
845  {
846  case SETOP_INTERSECT:
848  break;
849  case SETOP_EXCEPT:
851  break;
852  default:
853  elog(ERROR, "unrecognized set op: %d", (int) op->op);
854  cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */
855  break;
856  }
857  path = (Path *) create_setop_path(root,
858  result_rel,
859  path,
860  cmd,
861  use_hash ? SETOP_HASHED : SETOP_SORTED,
862  groupList,
863  list_length(op->colTypes) + 1,
864  use_hash ? firstFlag : -1,
865  dNumGroups,
866  dNumOutputRows);
867 
868  result_rel->rows = path->rows;
869  add_path(result_rel, path);
870  return result_rel;
871 }
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:264
#define Min(x, y)
Definition: c.h:993
SetOpCmd
Definition: nodes.h:386
@ SETOPCMD_EXCEPT
Definition: nodes.h:389
@ SETOPCMD_EXCEPT_ALL
Definition: nodes.h:390
@ SETOPCMD_INTERSECT_ALL
Definition: nodes.h:388
@ SETOPCMD_INTERSECT
Definition: nodes.h:387
@ SETOP_HASHED
Definition: nodes.h:396
@ SETOP_SORTED
Definition: nodes.h:395
@ SETOP_INTERSECT
Definition: parsenodes.h:1950
@ SETOP_EXCEPT
Definition: parsenodes.h:1951
List * make_pathkeys_for_sortclauses(PlannerInfo *root, List *sortclauses, List *tlist)
Definition: pathkeys.c:1349
AppendPath * create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *partial_subpaths, List *pathkeys, Relids required_outer, int parallel_workers, bool parallel_aware, double rows)
Definition: pathnode.c:1246
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2985
SetOpPath * create_setop_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, SetOpCmd cmd, SetOpStrategy strategy, List *distinctList, AttrNumber flagColIdx, int firstFlag, double numGroups, double outputRows)
Definition: pathnode.c:3537
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
@ UPPERREL_SETOP
Definition: pathnodes.h:71
#define list_make2(x1, x2)
Definition: pg_list.h:214
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1410
static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses, Path *input_path, double dNumGroups, double dNumOutputRows, const char *construct)
Definition: prepunion.c:1026
static List * generate_append_tlist(List *colTypes, List *colCollations, bool flag, List *input_tlists, List *refnames_tlist)
Definition: prepunion.c:1282
static RelOptInfo * recurse_set_operations(Node *setOp, PlannerInfo *root, List *colTypes, List *colCollations, bool junkOK, int flag, List *refnames_tlist, List **pTargetList, double *pNumGroups)
Definition: prepunion.c:208
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1463
Relids relids
Definition: pathnodes.h:856
struct PathTarget * reltarget
Definition: pathnodes.h:878
struct Path * cheapest_total_path
Definition: pathnodes.h:887
Cardinality rows
Definition: pathnodes.h:862
SetOperation op
Definition: parsenodes.h:2026
#define create_pathtarget(root, tlist)
Definition: tlist.h:53

References add_path(), SetOperationStmt::all, bms_union(), RelOptInfo::cheapest_total_path, choose_hashed_setop(), create_append_path(), create_pathtarget, create_setop_path(), create_sort_path(), elog(), ERROR, fetch_upper_rel(), generate_append_tlist(), generate_setop_grouplist(), SetOperationStmt::larg, list_length(), list_make2, make_pathkeys_for_sortclauses(), Min, NIL, SetOperationStmt::op, SetOperationStmt::rarg, recurse_set_operations(), RelOptInfo::relids, RelOptInfo::reltarget, RelOptInfo::rows, Path::rows, SETOP_EXCEPT, SETOP_HASHED, SETOP_INTERSECT, SETOP_SORTED, SETOPCMD_EXCEPT, SETOPCMD_EXCEPT_ALL, SETOPCMD_INTERSECT, SETOPCMD_INTERSECT_ALL, PlannerInfo::tuple_fraction, 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 444 of file prepunion.c.

447 {
448  RelOptInfo *result_rel;
449  Path *path;
450  RelOptInfo *lrel,
451  *rrel;
452  Path *lpath;
453  Path *rpath;
454  List *lpath_tlist;
455  List *rpath_tlist;
456  List *tlist;
457  List *groupList;
458  double dNumGroups;
459 
460  /* Parser should have rejected other cases */
461  if (setOp->op != SETOP_UNION)
462  elog(ERROR, "only UNION queries can be recursive");
463  /* Worktable ID should be assigned */
464  Assert(root->wt_param_id >= 0);
465 
466  /*
467  * Unlike a regular UNION node, process the left and right inputs
468  * separately without any intention of combining them into one Append.
469  */
470  lrel = recurse_set_operations(setOp->larg, root,
471  setOp->colTypes, setOp->colCollations,
472  false, -1,
473  refnames_tlist,
474  &lpath_tlist,
475  NULL);
476  lpath = lrel->cheapest_total_path;
477  /* The right path will want to look at the left one ... */
478  root->non_recursive_path = lpath;
479  rrel = recurse_set_operations(setOp->rarg, root,
480  setOp->colTypes, setOp->colCollations,
481  false, -1,
482  refnames_tlist,
483  &rpath_tlist,
484  NULL);
485  rpath = rrel->cheapest_total_path;
486  root->non_recursive_path = NULL;
487 
488  /*
489  * Generate tlist for RecursiveUnion path node --- same as in Append cases
490  */
491  tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations, false,
492  list_make2(lpath_tlist, rpath_tlist),
493  refnames_tlist);
494 
495  *pTargetList = tlist;
496 
497  /* Build result relation. */
498  result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
499  bms_union(lrel->relids, rrel->relids));
500  result_rel->reltarget = create_pathtarget(root, tlist);
501 
502  /*
503  * If UNION, identify the grouping operators
504  */
505  if (setOp->all)
506  {
507  groupList = NIL;
508  dNumGroups = 0;
509  }
510  else
511  {
512  /* Identify the grouping semantics */
513  groupList = generate_setop_grouplist(setOp, tlist);
514 
515  /* We only support hashing here */
516  if (!grouping_is_hashable(groupList))
517  ereport(ERROR,
518  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
519  errmsg("could not implement recursive UNION"),
520  errdetail("All column datatypes must be hashable.")));
521 
522  /*
523  * For the moment, take the number of distinct groups as equal to the
524  * total input size, ie, the worst case.
525  */
526  dNumGroups = lpath->rows + rpath->rows * 10;
527  }
528 
529  /*
530  * And make the path node.
531  */
532  path = (Path *) create_recursiveunion_path(root,
533  result_rel,
534  lpath,
535  rpath,
536  result_rel->reltarget,
537  groupList,
538  root->wt_param_id,
539  dNumGroups);
540 
541  add_path(result_rel, path);
542  postprocess_setop_rel(root, result_rel);
543  return result_rel;
544 }
@ SETOP_UNION
Definition: parsenodes.h:1949
RecursiveUnionPath * create_recursiveunion_path(PlannerInfo *root, RelOptInfo *rel, Path *leftpath, Path *rightpath, PathTarget *target, List *distinctList, int wtParam, double numGroups)
Definition: pathnode.c:3599
static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
Definition: prepunion.c:1008
struct Path * non_recursive_path
Definition: pathnodes.h:523
int wt_param_id
Definition: pathnodes.h:521

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

Referenced by plan_set_operations().

◆ generate_setop_grouplist()

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

Definition at line 1410 of file prepunion.c.

1411 {
1412  List *grouplist = copyObject(op->groupClauses);
1413  ListCell *lg;
1414  ListCell *lt;
1415 
1416  lg = list_head(grouplist);
1417  foreach(lt, targetlist)
1418  {
1419  TargetEntry *tle = (TargetEntry *) lfirst(lt);
1420  SortGroupClause *sgc;
1421 
1422  if (tle->resjunk)
1423  {
1424  /* resjunk columns should not have sortgrouprefs */
1425  Assert(tle->ressortgroupref == 0);
1426  continue; /* ignore resjunk columns */
1427  }
1428 
1429  /* non-resjunk columns should have sortgroupref = resno */
1430  Assert(tle->ressortgroupref == tle->resno);
1431 
1432  /* non-resjunk columns should have grouping clauses */
1433  Assert(lg != NULL);
1434  sgc = (SortGroupClause *) lfirst(lg);
1435  lg = lnext(grouplist, lg);
1436  Assert(sgc->tleSortGroupRef == 0);
1437 
1438  sgc->tleSortGroupRef = tle->ressortgroupref;
1439  }
1440  Assert(lg == NULL);
1441  return grouplist;
1442 }
#define copyObject(obj)
Definition: nodes.h:223
Index tleSortGroupRef
Definition: parsenodes.h:1385

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

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

◆ generate_setop_tlist()

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

Definition at line 1133 of file prepunion.c.

1140 {
1141  List *tlist = NIL;
1142  int resno = 1;
1143  ListCell *ctlc,
1144  *cclc,
1145  *itlc,
1146  *rtlc;
1147  TargetEntry *tle;
1148  Node *expr;
1149 
1150  *trivial_tlist = true; /* until proven differently */
1151 
1152  forfour(ctlc, colTypes, cclc, colCollations,
1153  itlc, input_tlist, rtlc, refnames_tlist)
1154  {
1155  Oid colType = lfirst_oid(ctlc);
1156  Oid colColl = lfirst_oid(cclc);
1157  TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
1158  TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
1159 
1160  Assert(inputtle->resno == resno);
1161  Assert(reftle->resno == resno);
1162  Assert(!inputtle->resjunk);
1163  Assert(!reftle->resjunk);
1164 
1165  /*
1166  * Generate columns referencing input columns and having appropriate
1167  * data types and column names. Insert datatype coercions where
1168  * necessary.
1169  *
1170  * HACK: constants in the input's targetlist are copied up as-is
1171  * rather than being referenced as subquery outputs. This is mainly
1172  * to ensure that when we try to coerce them to the output column's
1173  * datatype, the right things happen for UNKNOWN constants. But do
1174  * this only at the first level of subquery-scan plans; we don't want
1175  * phony constants appearing in the output tlists of upper-level
1176  * nodes!
1177  *
1178  * Note that copying a constant doesn't in itself require us to mark
1179  * the tlist nontrivial; see trivial_subqueryscan() in setrefs.c.
1180  */
1181  if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
1182  expr = (Node *) inputtle->expr;
1183  else
1184  expr = (Node *) makeVar(varno,
1185  inputtle->resno,
1186  exprType((Node *) inputtle->expr),
1187  exprTypmod((Node *) inputtle->expr),
1188  exprCollation((Node *) inputtle->expr),
1189  0);
1190 
1191  if (exprType(expr) != colType)
1192  {
1193  /*
1194  * Note: it's not really cool to be applying coerce_to_common_type
1195  * here; one notable point is that assign_expr_collations never
1196  * gets run on any generated nodes. For the moment that's not a
1197  * problem because we force the correct exposed collation below.
1198  * It would likely be best to make the parser generate the correct
1199  * output tlist for every set-op to begin with, though.
1200  */
1201  expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */
1202  expr,
1203  colType,
1204  "UNION/INTERSECT/EXCEPT");
1205  *trivial_tlist = false; /* the coercion makes it not trivial */
1206  }
1207 
1208  /*
1209  * Ensure the tlist entry's exposed collation matches the set-op. This
1210  * is necessary because plan_set_operations() reports the result
1211  * ordering as a list of SortGroupClauses, which don't carry collation
1212  * themselves but just refer to tlist entries. If we don't show the
1213  * right collation then planner.c might do the wrong thing in
1214  * higher-level queries.
1215  *
1216  * Note we use RelabelType, not CollateExpr, since this expression
1217  * will reach the executor without any further processing.
1218  */
1219  if (exprCollation(expr) != colColl)
1220  {
1221  expr = applyRelabelType(expr,
1222  exprType(expr), exprTypmod(expr), colColl,
1223  COERCE_IMPLICIT_CAST, -1, false);
1224  *trivial_tlist = false; /* the relabel makes it not trivial */
1225  }
1226 
1227  tle = makeTargetEntry((Expr *) expr,
1228  (AttrNumber) resno++,
1229  pstrdup(reftle->resname),
1230  false);
1231 
1232  /*
1233  * By convention, all non-resjunk columns in a setop tree have
1234  * ressortgroupref equal to their resno. In some cases the ref isn't
1235  * needed, but this is a cleaner way than modifying the tlist later.
1236  */
1237  tle->ressortgroupref = tle->resno;
1238 
1239  tlist = lappend(tlist, tle);
1240  }
1241 
1242  if (flag >= 0)
1243  {
1244  /* Add a resjunk flag column */
1245  /* flag value is the given constant */
1246  expr = (Node *) makeConst(INT4OID,
1247  -1,
1248  InvalidOid,
1249  sizeof(int32),
1251  false,
1252  true);
1253  tle = makeTargetEntry((Expr *) expr,
1254  (AttrNumber) resno++,
1255  pstrdup("flag"),
1256  true);
1257  tlist = lappend(tlist, tle);
1258  *trivial_tlist = false; /* the extra entry makes it not trivial */
1259  }
1260 
1261  return tlist;
1262 }
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:302
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:786
Node * applyRelabelType(Node *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat, int rlocation, bool overwrite_ok)
Definition: nodeFuncs.c:601
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
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
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:671

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

Referenced by recurse_set_operations().

◆ generate_union_paths()

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

Definition at line 550 of file prepunion.c.

553 {
554  Relids relids = NULL;
555  RelOptInfo *result_rel;
556  double save_fraction = root->tuple_fraction;
557  ListCell *lc;
558  List *pathlist = NIL;
559  List *partial_pathlist = NIL;
560  bool partial_paths_valid = true;
561  bool consider_parallel = true;
562  List *rellist;
563  List *tlist_list;
564  List *tlist;
565  Path *path;
566 
567  /*
568  * If plain UNION, tell children to fetch all tuples.
569  *
570  * Note: in UNION ALL, we pass the top-level tuple_fraction unmodified to
571  * each arm of the UNION ALL. One could make a case for reducing the
572  * tuple fraction for later arms (discounting by the expected size of the
573  * earlier arms' results) but it seems not worth the trouble. The normal
574  * case where tuple_fraction isn't already zero is a LIMIT at top level,
575  * and passing it down as-is is usually enough to get the desired result
576  * of preferring fast-start plans.
577  */
578  if (!op->all)
579  root->tuple_fraction = 0.0;
580 
581  /*
582  * If any of my children are identical UNION nodes (same op, all-flag, and
583  * colTypes) then they can be merged into this node so that we generate
584  * only one Append and unique-ification for the lot. Recurse to find such
585  * nodes and compute their children's paths.
586  */
587  rellist = plan_union_children(root, op, refnames_tlist, &tlist_list);
588 
589  /*
590  * Generate tlist for Append plan node.
591  *
592  * The tlist for an Append plan isn't important as far as the Append is
593  * concerned, but we must make it look real anyway for the benefit of the
594  * next plan level up.
595  */
596  tlist = generate_append_tlist(op->colTypes, op->colCollations, false,
597  tlist_list, refnames_tlist);
598 
599  *pTargetList = tlist;
600 
601  /* Build path lists and relid set. */
602  foreach(lc, rellist)
603  {
604  RelOptInfo *rel = lfirst(lc);
605 
606  pathlist = lappend(pathlist, rel->cheapest_total_path);
607 
608  if (consider_parallel)
609  {
610  if (!rel->consider_parallel)
611  {
612  consider_parallel = false;
613  partial_paths_valid = false;
614  }
615  else if (rel->partial_pathlist == NIL)
616  partial_paths_valid = false;
617  else
618  partial_pathlist = lappend(partial_pathlist,
619  linitial(rel->partial_pathlist));
620  }
621 
622  relids = bms_union(relids, rel->relids);
623  }
624 
625  /* Build result relation. */
626  result_rel = fetch_upper_rel(root, UPPERREL_SETOP, relids);
627  result_rel->reltarget = create_pathtarget(root, tlist);
628  result_rel->consider_parallel = consider_parallel;
629 
630  /*
631  * Append the child results together.
632  */
633  path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
634  NIL, NULL, 0, false, -1);
635 
636  /*
637  * For UNION ALL, we just need the Append path. For UNION, need to add
638  * node(s) to remove duplicates.
639  */
640  if (!op->all)
641  path = make_union_unique(op, path, tlist, root);
642 
643  add_path(result_rel, path);
644 
645  /*
646  * Estimate number of groups. For now we just assume the output is unique
647  * --- this is certainly true for the UNION case, and we want worst-case
648  * estimates anyway.
649  */
650  result_rel->rows = path->rows;
651 
652  /*
653  * Now consider doing the same thing using the partial paths plus Append
654  * plus Gather.
655  */
656  if (partial_paths_valid)
657  {
658  Path *ppath;
659  int parallel_workers = 0;
660 
661  /* Find the highest number of workers requested for any subpath. */
662  foreach(lc, partial_pathlist)
663  {
664  Path *subpath = lfirst(lc);
665 
666  parallel_workers = Max(parallel_workers,
667  subpath->parallel_workers);
668  }
669  Assert(parallel_workers > 0);
670 
671  /*
672  * If the use of parallel append is permitted, always request at least
673  * log2(# of children) paths. We assume it can be useful to have
674  * extra workers in this case because they will be spread out across
675  * the children. The precise formula is just a guess; see
676  * add_paths_to_append_rel.
677  */
679  {
680  parallel_workers = Max(parallel_workers,
681  pg_leftmost_one_pos32(list_length(partial_pathlist)) + 1);
682  parallel_workers = Min(parallel_workers,
684  }
685  Assert(parallel_workers > 0);
686 
687  ppath = (Path *)
688  create_append_path(root, result_rel, NIL, partial_pathlist,
689  NIL, NULL,
690  parallel_workers, enable_parallel_append,
691  -1);
692  ppath = (Path *)
693  create_gather_path(root, result_rel, ppath,
694  result_rel->reltarget, NULL, NULL);
695  if (!op->all)
696  ppath = make_union_unique(op, ppath, tlist, root);
697  add_path(result_rel, ppath);
698  }
699 
700  /* Undo effects of possibly forcing tuple_fraction to 0 */
701  root->tuple_fraction = save_fraction;
702 
703  return result_rel;
704 }
#define Max(x, y)
Definition: c.h:987
int max_parallel_workers_per_gather
Definition: costsize.c:133
bool enable_parallel_append
Definition: costsize.c:151
Datum subpath(PG_FUNCTION_ARGS)
Definition: ltree_op.c:241
GatherPath * create_gather_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target, Relids required_outer, double *rows)
Definition: pathnode.c:1968
static int pg_leftmost_one_pos32(uint32 word)
Definition: pg_bitutils.h:41
#define linitial(l)
Definition: pg_list.h:178
static Path * make_union_unique(SetOperationStmt *op, Path *path, List *tlist, PlannerInfo *root)
Definition: prepunion.c:946
static List * plan_union_children(PlannerInfo *root, SetOperationStmt *top_union, List *refnames_tlist, List **tlist_list)
Definition: prepunion.c:887
bool consider_parallel
Definition: pathnodes.h:872
List * partial_pathlist
Definition: pathnodes.h:885

References add_path(), SetOperationStmt::all, Assert(), bms_union(), RelOptInfo::cheapest_total_path, RelOptInfo::consider_parallel, create_append_path(), create_gather_path(), create_pathtarget, enable_parallel_append, fetch_upper_rel(), generate_append_tlist(), lappend(), lfirst, linitial, list_length(), make_union_unique(), Max, max_parallel_workers_per_gather, Min, NIL, RelOptInfo::partial_pathlist, pg_leftmost_one_pos32(), plan_union_children(), RelOptInfo::relids, RelOptInfo::reltarget, RelOptInfo::rows, Path::rows, subpath(), PlannerInfo::tuple_fraction, and UPPERREL_SETOP.

Referenced by recurse_set_operations().

◆ make_union_unique()

static Path * make_union_unique ( SetOperationStmt op,
Path path,
List tlist,
PlannerInfo root 
)
static

Definition at line 946 of file prepunion.c.

948 {
949  RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
950  List *groupList;
951  double dNumGroups;
952 
953  /* Identify the grouping semantics */
954  groupList = generate_setop_grouplist(op, tlist);
955 
956  /*
957  * XXX for the moment, take the number of distinct groups as equal to the
958  * total input size, ie, the worst case. This is too conservative, but
959  * it's not clear how to get a decent estimate of the true size. One
960  * should note as well the propensity of novices to write UNION rather
961  * than UNION ALL even when they don't expect any duplicates...
962  */
963  dNumGroups = path->rows;
964 
965  /* Decide whether to hash or sort */
966  if (choose_hashed_setop(root, groupList, path,
967  dNumGroups, dNumGroups,
968  "UNION"))
969  {
970  /* Hashed aggregate plan --- no sort needed */
971  path = (Path *) create_agg_path(root,
972  result_rel,
973  path,
974  create_pathtarget(root, tlist),
975  AGG_HASHED,
977  groupList,
978  NIL,
979  NULL,
980  dNumGroups);
981  }
982  else
983  {
984  /* Sort and Unique */
985  if (groupList)
986  path = (Path *)
987  create_sort_path(root,
988  result_rel,
989  path,
991  groupList,
992  tlist),
993  -1.0);
994  path = (Path *) create_upper_unique_path(root,
995  result_rel,
996  path,
997  list_length(path->pathkeys),
998  dNumGroups);
999  }
1000 
1001  return path;
1002 }
@ AGGSPLIT_SIMPLE
Definition: nodes.h:366
UpperUniquePath * create_upper_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, int numCols, double numGroups)
Definition: pathnode.c:3088
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:3140
List * pathkeys
Definition: pathnodes.h:1645

References AGG_HASHED, AGGSPLIT_SIMPLE, choose_hashed_setop(), create_agg_path(), create_pathtarget, create_sort_path(), create_upper_unique_path(), fetch_upper_rel(), generate_setop_grouplist(), list_length(), make_pathkeys_for_sortclauses(), NIL, Path::pathkeys, Path::rows, and UPPERREL_SETOP.

Referenced by generate_union_paths().

◆ plan_set_operations()

RelOptInfo* plan_set_operations ( PlannerInfo root)

Definition at line 104 of file prepunion.c.

105 {
106  Query *parse = root->parse;
107  SetOperationStmt *topop = castNode(SetOperationStmt, parse->setOperations);
108  Node *node;
109  RangeTblEntry *leftmostRTE;
110  Query *leftmostQuery;
111  RelOptInfo *setop_rel;
112  List *top_tlist;
113 
114  Assert(topop);
115 
116  /* check for unsupported stuff */
117  Assert(parse->jointree->fromlist == NIL);
118  Assert(parse->jointree->quals == NULL);
119  Assert(parse->groupClause == NIL);
120  Assert(parse->havingQual == NULL);
121  Assert(parse->windowClause == NIL);
122  Assert(parse->distinctClause == NIL);
123 
124  /*
125  * In the outer query level, we won't have any true equivalences to deal
126  * with; but we do want to be able to make pathkeys, which will require
127  * single-member EquivalenceClasses. Indicate that EC merging is complete
128  * so that pathkeys.c won't complain.
129  */
130  Assert(root->eq_classes == NIL);
131  root->ec_merging_done = true;
132 
133  /*
134  * We'll need to build RelOptInfos for each of the leaf subqueries, which
135  * are RTE_SUBQUERY rangetable entries in this Query. Prepare the index
136  * arrays for those, and for AppendRelInfos in case they're needed.
137  */
139 
140  /*
141  * Find the leftmost component Query. We need to use its column names for
142  * all generated tlists (else SELECT INTO won't work right).
143  */
144  node = topop->larg;
145  while (node && IsA(node, SetOperationStmt))
146  node = ((SetOperationStmt *) node)->larg;
147  Assert(node && IsA(node, RangeTblRef));
148  leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex];
149  leftmostQuery = leftmostRTE->subquery;
150  Assert(leftmostQuery != NULL);
151 
152  /*
153  * If the topmost node is a recursive union, it needs special processing.
154  */
155  if (root->hasRecursion)
156  {
157  setop_rel = generate_recursion_path(topop, root,
158  leftmostQuery->targetList,
159  &top_tlist);
160  }
161  else
162  {
163  /*
164  * Recurse on setOperations tree to generate paths for set ops. The
165  * final output paths should have just the column types shown as the
166  * output from the top-level node, plus possibly resjunk working
167  * columns (we can rely on upper-level nodes to deal with that).
168  */
169  setop_rel = recurse_set_operations((Node *) topop, root,
170  topop->colTypes, topop->colCollations,
171  true, -1,
172  leftmostQuery->targetList,
173  &top_tlist,
174  NULL);
175  }
176 
177  /* Must return the built tlist into root->processed_tlist. */
178  root->processed_tlist = top_tlist;
179 
180  return setop_rel;
181 }
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
static RelOptInfo * generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition: prepunion.c:444
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:715
void setup_simple_rel_arrays(PlannerInfo *root)
Definition: relnode.c:94
List * processed_tlist
Definition: pathnodes.h:453
bool hasRecursion
Definition: pathnodes.h:501
bool ec_merging_done
Definition: pathnodes.h:314
List * eq_classes
Definition: pathnodes.h:311
Query * parse
Definition: pathnodes.h:199
List * targetList
Definition: parsenodes.h:181
Query * subquery
Definition: parsenodes.h:1073

References Assert(), castNode, PlannerInfo::ec_merging_done, PlannerInfo::eq_classes, generate_recursion_path(), PlannerInfo::hasRecursion, IsA, SetOperationStmt::larg, NIL, parse(), PlannerInfo::parse, PlannerInfo::processed_tlist, recurse_set_operations(), setup_simple_rel_arrays(), RangeTblEntry::subquery, and Query::targetList.

Referenced by grouping_planner().

◆ plan_union_children()

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

Definition at line 887 of file prepunion.c.

891 {
892  List *pending_rels = list_make1(top_union);
893  List *result = NIL;
894  List *child_tlist;
895 
896  *tlist_list = NIL;
897 
898  while (pending_rels != NIL)
899  {
900  Node *setOp = linitial(pending_rels);
901 
902  pending_rels = list_delete_first(pending_rels);
903 
904  if (IsA(setOp, SetOperationStmt))
905  {
906  SetOperationStmt *op = (SetOperationStmt *) setOp;
907 
908  if (op->op == top_union->op &&
909  (op->all == top_union->all || op->all) &&
910  equal(op->colTypes, top_union->colTypes))
911  {
912  /* Same UNION, so fold children into parent */
913  pending_rels = lcons(op->rarg, pending_rels);
914  pending_rels = lcons(op->larg, pending_rels);
915  continue;
916  }
917  }
918 
919  /*
920  * Not same, so plan this child separately.
921  *
922  * Note we disallow any resjunk columns in child results. This is
923  * necessary since the Append node that implements the union won't do
924  * any projection, and upper levels will get confused if some of our
925  * output tuples have junk and some don't. This case only arises when
926  * we have an EXCEPT or INTERSECT as child, else there won't be
927  * resjunk anyway.
928  */
929  result = lappend(result, recurse_set_operations(setOp, root,
930  top_union->colTypes,
931  top_union->colCollations,
932  false, -1,
933  refnames_tlist,
934  &child_tlist,
935  NULL));
936  *tlist_list = lappend(*tlist_list, child_tlist);
937  }
938 
939  return result;
940 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
List * list_delete_first(List *list)
Definition: list.c:943
List * lcons(void *datum, List *list)
Definition: list.c:495
#define list_make1(x1)
Definition: pg_list.h:212

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

Referenced by generate_union_paths().

◆ postprocess_setop_rel()

static void postprocess_setop_rel ( PlannerInfo root,
RelOptInfo rel 
)
static

Definition at line 1008 of file prepunion.c.

1009 {
1010  /*
1011  * We don't currently worry about allowing FDWs to contribute paths to
1012  * this relation, but give extensions a chance.
1013  */
1015  (*create_upper_paths_hook) (root, UPPERREL_SETOP,
1016  NULL, rel, NULL);
1017 
1018  /* Select cheapest path */
1019  set_cheapest(rel);
1020 }
void set_cheapest(RelOptInfo *parent_rel)
Definition: pathnode.c:244
create_upper_paths_hook_type create_upper_paths_hook
Definition: planner.c:80

References create_upper_paths_hook, set_cheapest(), and UPPERREL_SETOP.

Referenced by generate_recursion_path(), and recurse_set_operations().

◆ recurse_set_operations()

static RelOptInfo * recurse_set_operations ( Node setOp,
PlannerInfo root,
List colTypes,
List colCollations,
bool  junkOK,
int  flag,
List refnames_tlist,
List **  pTargetList,
double *  pNumGroups 
)
static

Definition at line 208 of file prepunion.c.

214 {
215  RelOptInfo *rel = NULL; /* keep compiler quiet */
216 
217  /* Guard against stack overflow due to overly complex setop nests */
219 
220  if (IsA(setOp, RangeTblRef))
221  {
222  RangeTblRef *rtr = (RangeTblRef *) setOp;
223  RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
224  Query *subquery = rte->subquery;
225  PlannerInfo *subroot;
226  RelOptInfo *final_rel;
227  Path *subpath;
228  Path *path;
229  List *tlist;
230  bool trivial_tlist;
231 
232  Assert(subquery != NULL);
233 
234  /* Build a RelOptInfo for this leaf subquery. */
235  rel = build_simple_rel(root, rtr->rtindex, NULL);
236 
237  /* plan_params should not be in use in current query level */
238  Assert(root->plan_params == NIL);
239 
240  /* Generate a subroot and Paths for the subquery */
241  subroot = rel->subroot = subquery_planner(root->glob, subquery,
242  root,
243  false,
244  root->tuple_fraction);
245 
246  /*
247  * It should not be possible for the primitive query to contain any
248  * cross-references to other primitive queries in the setop tree.
249  */
250  if (root->plan_params)
251  elog(ERROR, "unexpected outer reference in set operation subquery");
252 
253  /* Figure out the appropriate target list for this subquery. */
254  tlist = generate_setop_tlist(colTypes, colCollations,
255  flag,
256  rtr->rtindex,
257  true,
258  subroot->processed_tlist,
259  refnames_tlist,
260  &trivial_tlist);
261  rel->reltarget = create_pathtarget(root, tlist);
262 
263  /* Return the fully-fledged tlist to caller, too */
264  *pTargetList = tlist;
265 
266  /*
267  * Mark rel with estimated output rows, width, etc. Note that we have
268  * to do this before generating outer-query paths, else
269  * cost_subqueryscan is not happy.
270  */
271  set_subquery_size_estimates(root, rel);
272 
273  /*
274  * Since we may want to add a partial path to this relation, we must
275  * set its consider_parallel flag correctly.
276  */
277  final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
278  rel->consider_parallel = final_rel->consider_parallel;
279 
280  /*
281  * For the moment, we consider only a single Path for the subquery.
282  * This should change soon (make it look more like
283  * set_subquery_pathlist).
284  */
286  root->tuple_fraction);
287 
288  /*
289  * Stick a SubqueryScanPath atop that.
290  *
291  * We don't bother to determine the subquery's output ordering since
292  * it won't be reflected in the set-op result anyhow; so just label
293  * the SubqueryScanPath with nil pathkeys. (XXX that should change
294  * soon too, likely.)
295  */
296  path = (Path *) create_subqueryscan_path(root, rel, subpath,
297  trivial_tlist,
298  NIL, NULL);
299 
300  add_path(rel, path);
301 
302  /*
303  * If we have a partial path for the child relation, we can use that
304  * to build a partial path for this relation. But there's no point in
305  * considering any path but the cheapest.
306  */
307  if (rel->consider_parallel && bms_is_empty(rel->lateral_relids) &&
308  final_rel->partial_pathlist != NIL)
309  {
310  Path *partial_subpath;
311  Path *partial_path;
312 
313  partial_subpath = linitial(final_rel->partial_pathlist);
314  partial_path = (Path *)
315  create_subqueryscan_path(root, rel, partial_subpath,
316  trivial_tlist,
317  NIL, NULL);
318  add_partial_path(rel, partial_path);
319  }
320 
321  /*
322  * Estimate number of groups if caller wants it. If the subquery used
323  * grouping or aggregation, its output is probably mostly unique
324  * anyway; otherwise do statistical estimation.
325  *
326  * XXX you don't really want to know about this: we do the estimation
327  * using the subroot->parse's original targetlist expressions, not the
328  * subroot->processed_tlist which might seem more appropriate. The
329  * reason is that if the subquery is itself a setop, it may return a
330  * processed_tlist containing "varno 0" Vars generated by
331  * generate_append_tlist, and those would confuse estimate_num_groups
332  * mightily. We ought to get rid of the "varno 0" hack, but that
333  * requires a redesign of the parsetree representation of setops, so
334  * that there can be an RTE corresponding to each setop's output.
335  * Note, we use this not subquery's targetlist but subroot->parse's
336  * targetlist, because it was revised by self-join removal. subquery's
337  * targetlist might contain the references to the removed relids.
338  */
339  if (pNumGroups)
340  {
341  if (subquery->groupClause || subquery->groupingSets ||
342  subquery->distinctClause ||
343  subroot->hasHavingQual || subquery->hasAggs)
344  *pNumGroups = subpath->rows;
345  else
346  *pNumGroups = estimate_num_groups(subroot,
347  get_tlist_exprs(subroot->parse->targetList, false),
348  subpath->rows,
349  NULL,
350  NULL);
351  }
352  }
353  else if (IsA(setOp, SetOperationStmt))
354  {
355  SetOperationStmt *op = (SetOperationStmt *) setOp;
356 
357  /* UNIONs are much different from INTERSECT/EXCEPT */
358  if (op->op == SETOP_UNION)
359  rel = generate_union_paths(op, root,
360  refnames_tlist,
361  pTargetList);
362  else
363  rel = generate_nonunion_paths(op, root,
364  refnames_tlist,
365  pTargetList);
366  if (pNumGroups)
367  *pNumGroups = rel->rows;
368 
369  /*
370  * If necessary, add a Result node to project the caller-requested
371  * output columns.
372  *
373  * XXX you don't really want to know about this: setrefs.c will apply
374  * fix_upper_expr() to the Result node's tlist. This would fail if the
375  * Vars generated by generate_setop_tlist() were not exactly equal()
376  * to the corresponding tlist entries of the subplan. However, since
377  * the subplan was generated by generate_union_paths() or
378  * generate_nonunion_paths(), and hence its tlist was generated by
379  * generate_append_tlist(), this will work. We just tell
380  * generate_setop_tlist() to use varno 0.
381  */
382  if (flag >= 0 ||
383  !tlist_same_datatypes(*pTargetList, colTypes, junkOK) ||
384  !tlist_same_collations(*pTargetList, colCollations, junkOK))
385  {
386  PathTarget *target;
387  bool trivial_tlist;
388  ListCell *lc;
389 
390  *pTargetList = generate_setop_tlist(colTypes, colCollations,
391  flag,
392  0,
393  false,
394  *pTargetList,
395  refnames_tlist,
396  &trivial_tlist);
397  target = create_pathtarget(root, *pTargetList);
398 
399  /* Apply projection to each path */
400  foreach(lc, rel->pathlist)
401  {
402  Path *subpath = (Path *) lfirst(lc);
403  Path *path;
404 
405  Assert(subpath->param_info == NULL);
406  path = apply_projection_to_path(root, subpath->parent,
407  subpath, target);
408  /* If we had to add a Result, path is different from subpath */
409  if (path != subpath)
410  lfirst(lc) = path;
411  }
412 
413  /* Apply projection to each partial path */
414  foreach(lc, rel->partial_pathlist)
415  {
416  Path *subpath = (Path *) lfirst(lc);
417  Path *path;
418 
419  Assert(subpath->param_info == NULL);
420 
421  /* avoid apply_projection_to_path, in case of multiple refs */
422  path = (Path *) create_projection_path(root, subpath->parent,
423  subpath, target);
424  lfirst(lc) = path;
425  }
426  }
427  }
428  else
429  {
430  elog(ERROR, "unrecognized node type: %d",
431  (int) nodeTag(setOp));
432  *pTargetList = NIL;
433  }
434 
435  postprocess_setop_rel(root, rel);
436 
437  return rel;
438 }
#define bms_is_empty(a)
Definition: bitmapset.h:105
void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition: costsize.c:5823
#define nodeTag(nodeptr)
Definition: nodes.h:133
ProjectionPath * create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target)
Definition: pathnode.c:2670
SubqueryScanPath * create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, bool trivial_pathtarget, List *pathkeys, Relids required_outer)
Definition: pathnode.c:2012
void add_partial_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:749
Path * apply_projection_to_path(PlannerInfo *root, RelOptInfo *rel, Path *path, PathTarget *target)
Definition: pathnode.c:2778
@ UPPERREL_FINAL
Definition: pathnodes.h:79
PlannerInfo * subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, bool hasRecursion, double tuple_fraction)
Definition: planner.c:625
Path * get_cheapest_fractional_path(RelOptInfo *rel, double tuple_fraction)
Definition: planner.c:6248
void check_stack_depth(void)
Definition: postgres.c:3523
static List * generate_setop_tlist(List *colTypes, List *colCollations, int flag, Index varno, bool hack_constants, List *input_tlist, List *refnames_tlist, bool *trivial_tlist)
Definition: prepunion.c:1133
static RelOptInfo * generate_union_paths(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition: prepunion.c:550
static RelOptInfo * generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition: prepunion.c:710
RelOptInfo * build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
Definition: relnode.c:192
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset, EstimationInfo *estinfo)
Definition: selfuncs.c:3416
bool hasHavingQual
Definition: pathnodes.h:493
PlannerGlobal * glob
Definition: pathnodes.h:202
List * plan_params
Definition: pathnodes.h:217
List * groupClause
Definition: parsenodes.h:190
List * groupingSets
Definition: parsenodes.h:193
List * distinctClause
Definition: parsenodes.h:199
Relids lateral_relids
Definition: pathnodes.h:898
List * pathlist
Definition: pathnodes.h:883
PlannerInfo * subroot
Definition: pathnodes.h:934
bool tlist_same_collations(List *tlist, List *colCollations, bool junkOK)
Definition: tlist.c:282
List * get_tlist_exprs(List *tlist, bool includeJunk)
Definition: tlist.c:163
bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
Definition: tlist.c:248

References add_partial_path(), add_path(), apply_projection_to_path(), Assert(), bms_is_empty, build_simple_rel(), check_stack_depth(), RelOptInfo::consider_parallel, create_pathtarget, create_projection_path(), create_subqueryscan_path(), Query::distinctClause, elog(), ERROR, estimate_num_groups(), fetch_upper_rel(), flag(), generate_nonunion_paths(), generate_setop_tlist(), generate_union_paths(), get_cheapest_fractional_path(), get_tlist_exprs(), PlannerInfo::glob, Query::groupClause, Query::groupingSets, PlannerInfo::hasHavingQual, IsA, RelOptInfo::lateral_relids, lfirst, linitial, NIL, nodeTag, SetOperationStmt::op, PlannerInfo::parse, RelOptInfo::partial_pathlist, RelOptInfo::pathlist, PlannerInfo::plan_params, postprocess_setop_rel(), PlannerInfo::processed_tlist, RelOptInfo::reltarget, RelOptInfo::rows, RangeTblRef::rtindex, set_subquery_size_estimates(), SETOP_UNION, subpath(), RangeTblEntry::subquery, subquery_planner(), RelOptInfo::subroot, Query::targetList, tlist_same_collations(), tlist_same_datatypes(), PlannerInfo::tuple_fraction, and UPPERREL_FINAL.

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