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

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

Referenced by generate_nonunion_paths(), and make_union_unique().

1020 {
1021  int numGroupCols = list_length(groupClauses);
1022  bool can_sort;
1023  bool can_hash;
1024  Size hashentrysize;
1025  Path hashed_p;
1026  Path sorted_p;
1027  double tuple_fraction;
1028 
1029  /* Check whether the operators support sorting or hashing */
1030  can_sort = grouping_is_sortable(groupClauses);
1031  can_hash = grouping_is_hashable(groupClauses);
1032  if (can_hash && can_sort)
1033  {
1034  /* we have a meaningful choice to make, continue ... */
1035  }
1036  else if (can_hash)
1037  return true;
1038  else if (can_sort)
1039  return false;
1040  else
1041  ereport(ERROR,
1042  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1043  /* translator: %s is UNION, INTERSECT, or EXCEPT */
1044  errmsg("could not implement %s", construct),
1045  errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
1046 
1047  /* Prefer sorting when enable_hashagg is off */
1048  if (!enable_hashagg)
1049  return false;
1050 
1051  /*
1052  * Don't do it if it doesn't look like the hashtable will fit into
1053  * work_mem.
1054  */
1055  hashentrysize = MAXALIGN(input_path->pathtarget->width) + MAXALIGN(SizeofMinimalTupleHeader);
1056 
1057  if (hashentrysize * dNumGroups > work_mem * 1024L)
1058  return false;
1059 
1060  /*
1061  * See if the estimated cost is no more than doing it the other way.
1062  *
1063  * We need to consider input_plan + hashagg versus input_plan + sort +
1064  * group. Note that the actual result plan might involve a SetOp or
1065  * Unique node, not Agg or Group, but the cost estimates for Agg and Group
1066  * should be close enough for our purposes here.
1067  *
1068  * These path variables are dummies that just hold cost fields; we don't
1069  * make actual Paths for these steps.
1070  */
1071  cost_agg(&hashed_p, root, AGG_HASHED, NULL,
1072  numGroupCols, dNumGroups,
1073  NIL,
1074  input_path->startup_cost, input_path->total_cost,
1075  input_path->rows);
1076 
1077  /*
1078  * Now for the sorted case. Note that the input is *always* unsorted,
1079  * since it was made by appending unrelated sub-relations together.
1080  */
1081  sorted_p.startup_cost = input_path->startup_cost;
1082  sorted_p.total_cost = input_path->total_cost;
1083  /* XXX cost_sort doesn't actually look at pathkeys, so just pass NIL */
1084  cost_sort(&sorted_p, root, NIL, sorted_p.total_cost,
1085  input_path->rows, input_path->pathtarget->width,
1086  0.0, work_mem, -1.0);
1087  cost_group(&sorted_p, root, numGroupCols, dNumGroups,
1088  NIL,
1089  sorted_p.startup_cost, sorted_p.total_cost,
1090  input_path->rows);
1091 
1092  /*
1093  * Now make the decision using the top-level tuple fraction. First we
1094  * have to convert an absolute count (LIMIT) into fractional form.
1095  */
1096  tuple_fraction = root->tuple_fraction;
1097  if (tuple_fraction >= 1.0)
1098  tuple_fraction /= dNumOutputRows;
1099 
1100  if (compare_fractional_path_costs(&hashed_p, &sorted_p,
1101  tuple_fraction) < 0)
1102  {
1103  /* Hashed is cheaper, so use it */
1104  return true;
1105  }
1106  return false;
1107 }
#define NIL
Definition: pg_list.h:65
PathTarget * pathtarget
Definition: pathnodes.h:1115
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)
Definition: costsize.c:2151
int errcode(int sqlerrcode)
Definition: elog.c:570
bool grouping_is_hashable(List *groupClause)
Definition: tlist.c:582
double tuple_fraction
Definition: pathnodes.h:334
#define ERROR
Definition: elog.h:43
Cost startup_cost
Definition: pathnodes.h:1125
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:2346
int errdetail(const char *fmt,...)
Definition: elog.c:860
#define ereport(elevel, rest)
Definition: elog.h:141
#define SizeofMinimalTupleHeader
Definition: htup_details.h:649
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:1693
int work_mem
Definition: globals.c:121
Cost total_cost
Definition: pathnodes.h:1126
double rows
Definition: pathnodes.h:1124
int compare_fractional_path_costs(Path *path1, Path *path2, double fraction)
Definition: pathnode.c:118
size_t Size
Definition: c.h:466
static int list_length(const List *l)
Definition: pg_list.h:169
#define MAXALIGN(LEN)
Definition: c.h:685
bool enable_hashagg
Definition: costsize.c:130
int errmsg(const char *fmt,...)
Definition: elog.c:784
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:562

◆ generate_append_tlist()

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

Definition at line 1263 of file prepunion.c.

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

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

1267 {
1268  List *tlist = NIL;
1269  int resno = 1;
1270  ListCell *curColType;
1271  ListCell *curColCollation;
1272  ListCell *ref_tl_item;
1273  int colindex;
1274  TargetEntry *tle;
1275  Node *expr;
1276  ListCell *tlistl;
1277  int32 *colTypmods;
1278 
1279  /*
1280  * First extract typmods to use.
1281  *
1282  * If the inputs all agree on type and typmod of a particular column, use
1283  * that typmod; else use -1.
1284  */
1285  colTypmods = (int32 *) palloc(list_length(colTypes) * sizeof(int32));
1286 
1287  foreach(tlistl, input_tlists)
1288  {
1289  List *subtlist = (List *) lfirst(tlistl);
1290  ListCell *subtlistl;
1291 
1292  curColType = list_head(colTypes);
1293  colindex = 0;
1294  foreach(subtlistl, subtlist)
1295  {
1296  TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
1297 
1298  if (subtle->resjunk)
1299  continue;
1300  Assert(curColType != NULL);
1301  if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1302  {
1303  /* If first subplan, copy the typmod; else compare */
1304  int32 subtypmod = exprTypmod((Node *) subtle->expr);
1305 
1306  if (tlistl == list_head(input_tlists))
1307  colTypmods[colindex] = subtypmod;
1308  else if (subtypmod != colTypmods[colindex])
1309  colTypmods[colindex] = -1;
1310  }
1311  else
1312  {
1313  /* types disagree, so force typmod to -1 */
1314  colTypmods[colindex] = -1;
1315  }
1316  curColType = lnext(colTypes, curColType);
1317  colindex++;
1318  }
1319  Assert(curColType == NULL);
1320  }
1321 
1322  /*
1323  * Now we can build the tlist for the Append.
1324  */
1325  colindex = 0;
1326  forthree(curColType, colTypes, curColCollation, colCollations,
1327  ref_tl_item, refnames_tlist)
1328  {
1329  Oid colType = lfirst_oid(curColType);
1330  int32 colTypmod = colTypmods[colindex++];
1331  Oid colColl = lfirst_oid(curColCollation);
1332  TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
1333 
1334  Assert(reftle->resno == resno);
1335  Assert(!reftle->resjunk);
1336  expr = (Node *) makeVar(0,
1337  resno,
1338  colType,
1339  colTypmod,
1340  colColl,
1341  0);
1342  tle = makeTargetEntry((Expr *) expr,
1343  (AttrNumber) resno++,
1344  pstrdup(reftle->resname),
1345  false);
1346 
1347  /*
1348  * By convention, all non-resjunk columns in a setop tree have
1349  * ressortgroupref equal to their resno. In some cases the ref isn't
1350  * needed, but this is a cleaner way than modifying the tlist later.
1351  */
1352  tle->ressortgroupref = tle->resno;
1353 
1354  tlist = lappend(tlist, tle);
1355  }
1356 
1357  if (flag)
1358  {
1359  /* Add a resjunk flag column */
1360  /* flag value is shown as copied up from subplan */
1361  expr = (Node *) makeVar(0,
1362  resno,
1363  INT4OID,
1364  -1,
1365  InvalidOid,
1366  0);
1367  tle = makeTargetEntry((Expr *) expr,
1368  (AttrNumber) resno++,
1369  pstrdup("flag"),
1370  true);
1371  tlist = lappend(tlist, tle);
1372  }
1373 
1374  pfree(colTypmods);
1375 
1376  return tlist;
1377 }
#define NIL
Definition: pg_list.h:65
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
int32 exprTypmod(const Node *expr)
Definition: nodeFuncs.c:276
#define forthree(cell1, list1, cell2, list2, cell3, list3)
Definition: pg_list.h:464
char * pstrdup(const char *in)
Definition: mcxt.c:1186
Definition: nodes.h:525
unsigned int Oid
Definition: postgres_ext.h:31
char * resname
Definition: primnodes.h:1395
signed int int32
Definition: c.h:346
void pfree(void *pointer)
Definition: mcxt.c:1056
bool resjunk
Definition: primnodes.h:1400
char * flag(int b)
Definition: test-ctype.c:33
AttrNumber resno
Definition: primnodes.h:1394
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:236
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
List * lappend(List *list, void *datum)
Definition: list.c:322
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
Expr * expr
Definition: primnodes.h:1393
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
static int list_length(const List *l)
Definition: pg_list.h:169
void * palloc(Size size)
Definition: mcxt.c:949
Index ressortgroupref
Definition: primnodes.h:1396
Definition: pg_list.h:50
int16 AttrNumber
Definition: attnum.h:21
#define lfirst_oid(lc)
Definition: pg_list.h:192

◆ generate_nonunion_paths()

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

Definition at line 699 of file prepunion.c.

References add_path(), SetOperationStmt::all, bms_union(), RelOptInfo::cheapest_total_path, choose_hashed_setop(), SetOperationStmt::colCollations, SetOperationStmt::colTypes, 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().

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

◆ generate_recursion_path()

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

Definition at line 433 of file prepunion.c.

References add_path(), SetOperationStmt::all, Assert, bms_union(), RelOptInfo::cheapest_total_path, SetOperationStmt::colCollations, SetOperationStmt::colTypes, 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().

436 {
437  RelOptInfo *result_rel;
438  Path *path;
439  RelOptInfo *lrel,
440  *rrel;
441  Path *lpath;
442  Path *rpath;
443  List *lpath_tlist;
444  List *rpath_tlist;
445  List *tlist;
446  List *groupList;
447  double dNumGroups;
448 
449  /* Parser should have rejected other cases */
450  if (setOp->op != SETOP_UNION)
451  elog(ERROR, "only UNION queries can be recursive");
452  /* Worktable ID should be assigned */
453  Assert(root->wt_param_id >= 0);
454 
455  /*
456  * Unlike a regular UNION node, process the left and right inputs
457  * separately without any intention of combining them into one Append.
458  */
459  lrel = recurse_set_operations(setOp->larg, root,
460  setOp->colTypes, setOp->colCollations,
461  false, -1,
462  refnames_tlist,
463  &lpath_tlist,
464  NULL);
465  lpath = lrel->cheapest_total_path;
466  /* The right path will want to look at the left one ... */
467  root->non_recursive_path = lpath;
468  rrel = recurse_set_operations(setOp->rarg, root,
469  setOp->colTypes, setOp->colCollations,
470  false, -1,
471  refnames_tlist,
472  &rpath_tlist,
473  NULL);
474  rpath = rrel->cheapest_total_path;
475  root->non_recursive_path = NULL;
476 
477  /*
478  * Generate tlist for RecursiveUnion path node --- same as in Append cases
479  */
480  tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations, false,
481  list_make2(lpath_tlist, rpath_tlist),
482  refnames_tlist);
483 
484  *pTargetList = tlist;
485 
486  /* Build result relation. */
487  result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
488  bms_union(lrel->relids, rrel->relids));
489  result_rel->reltarget = create_pathtarget(root, tlist);
490 
491  /*
492  * If UNION, identify the grouping operators
493  */
494  if (setOp->all)
495  {
496  groupList = NIL;
497  dNumGroups = 0;
498  }
499  else
500  {
501  /* Identify the grouping semantics */
502  groupList = generate_setop_grouplist(setOp, tlist);
503 
504  /* We only support hashing here */
505  if (!grouping_is_hashable(groupList))
506  ereport(ERROR,
507  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
508  errmsg("could not implement recursive UNION"),
509  errdetail("All column datatypes must be hashable.")));
510 
511  /*
512  * For the moment, take the number of distinct groups as equal to the
513  * total input size, ie, the worst case.
514  */
515  dNumGroups = lpath->rows + rpath->rows * 10;
516  }
517 
518  /*
519  * And make the path node.
520  */
521  path = (Path *) create_recursiveunion_path(root,
522  result_rel,
523  lpath,
524  rpath,
525  result_rel->reltarget,
526  groupList,
527  root->wt_param_id,
528  dNumGroups);
529 
530  add_path(result_rel, path);
531  postprocess_setop_rel(root, result_rel);
532  return result_rel;
533 }
#define list_make2(x1, x2)
Definition: pg_list.h:229
#define NIL
Definition: pg_list.h:65
static List * generate_append_tlist(List *colTypes, List *colCollations, bool flag, List *input_tlists, List *refnames_tlist)
Definition: prepunion.c:1263
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:423
static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
Definition: prepunion.c:998
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1391
int errcode(int sqlerrcode)
Definition: elog.c:570
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:207
bool grouping_is_hashable(List *groupClause)
Definition: tlist.c:582
int wt_param_id
Definition: pathnodes.h:351
#define ERROR
Definition: elog.h:43
RecursiveUnionPath * create_recursiveunion_path(PlannerInfo *root, RelOptInfo *rel, Path *leftpath, Path *rightpath, PathTarget *target, List *distinctList, int wtParam, double numGroups)
Definition: pathnode.c:3340
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1164
struct Path * cheapest_total_path
Definition: pathnodes.h:659
int errdetail(const char *fmt,...)
Definition: elog.c:860
#define create_pathtarget(root, tlist)
Definition: tlist.h:54
Relids relids
Definition: pathnodes.h:641
#define ereport(elevel, rest)
Definition: elog.h:141
List * colCollations
Definition: parsenodes.h:1643
#define Assert(condition)
Definition: c.h:732
double rows
Definition: pathnodes.h:1124
struct Path * non_recursive_path
Definition: pathnodes.h:352
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:225
SetOperation op
Definition: parsenodes.h:1634
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:652

◆ generate_setop_grouplist()

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

Definition at line 1391 of file prepunion.c.

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

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

1392 {
1393  List *grouplist = copyObject(op->groupClauses);
1394  ListCell *lg;
1395  ListCell *lt;
1396 
1397  lg = list_head(grouplist);
1398  foreach(lt, targetlist)
1399  {
1400  TargetEntry *tle = (TargetEntry *) lfirst(lt);
1401  SortGroupClause *sgc;
1402 
1403  if (tle->resjunk)
1404  {
1405  /* resjunk columns should not have sortgrouprefs */
1406  Assert(tle->ressortgroupref == 0);
1407  continue; /* ignore resjunk columns */
1408  }
1409 
1410  /* non-resjunk columns should have sortgroupref = resno */
1411  Assert(tle->ressortgroupref == tle->resno);
1412 
1413  /* non-resjunk columns should have grouping clauses */
1414  Assert(lg != NULL);
1415  sgc = (SortGroupClause *) lfirst(lg);
1416  lg = lnext(grouplist, lg);
1417  Assert(sgc->tleSortGroupRef == 0);
1418 
1419  sgc->tleSortGroupRef = tle->ressortgroupref;
1420  }
1421  Assert(lg == NULL);
1422  return grouplist;
1423 }
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
Index tleSortGroupRef
Definition: parsenodes.h:1234
bool resjunk
Definition: primnodes.h:1400
AttrNumber resno
Definition: primnodes.h:1394
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
Index ressortgroupref
Definition: primnodes.h:1396
#define copyObject(obj)
Definition: nodes.h:641
Definition: pg_list.h:50

◆ 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 
)
static

Definition at line 1121 of file prepunion.c.

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

Referenced by recurse_set_operations().

1127 {
1128  List *tlist = NIL;
1129  int resno = 1;
1130  ListCell *ctlc,
1131  *cclc,
1132  *itlc,
1133  *rtlc;
1134  TargetEntry *tle;
1135  Node *expr;
1136 
1137  forfour(ctlc, colTypes, cclc, colCollations,
1138  itlc, input_tlist, rtlc, refnames_tlist)
1139  {
1140  Oid colType = lfirst_oid(ctlc);
1141  Oid colColl = lfirst_oid(cclc);
1142  TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
1143  TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
1144 
1145  Assert(inputtle->resno == resno);
1146  Assert(reftle->resno == resno);
1147  Assert(!inputtle->resjunk);
1148  Assert(!reftle->resjunk);
1149 
1150  /*
1151  * Generate columns referencing input columns and having appropriate
1152  * data types and column names. Insert datatype coercions where
1153  * necessary.
1154  *
1155  * HACK: constants in the input's targetlist are copied up as-is
1156  * rather than being referenced as subquery outputs. This is mainly
1157  * to ensure that when we try to coerce them to the output column's
1158  * datatype, the right things happen for UNKNOWN constants. But do
1159  * this only at the first level of subquery-scan plans; we don't want
1160  * phony constants appearing in the output tlists of upper-level
1161  * nodes!
1162  */
1163  if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
1164  expr = (Node *) inputtle->expr;
1165  else
1166  expr = (Node *) makeVar(varno,
1167  inputtle->resno,
1168  exprType((Node *) inputtle->expr),
1169  exprTypmod((Node *) inputtle->expr),
1170  exprCollation((Node *) inputtle->expr),
1171  0);
1172 
1173  if (exprType(expr) != colType)
1174  {
1175  /*
1176  * Note: it's not really cool to be applying coerce_to_common_type
1177  * here; one notable point is that assign_expr_collations never
1178  * gets run on any generated nodes. For the moment that's not a
1179  * problem because we force the correct exposed collation below.
1180  * It would likely be best to make the parser generate the correct
1181  * output tlist for every set-op to begin with, though.
1182  */
1183  expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */
1184  expr,
1185  colType,
1186  "UNION/INTERSECT/EXCEPT");
1187  }
1188 
1189  /*
1190  * Ensure the tlist entry's exposed collation matches the set-op. This
1191  * is necessary because plan_set_operations() reports the result
1192  * ordering as a list of SortGroupClauses, which don't carry collation
1193  * themselves but just refer to tlist entries. If we don't show the
1194  * right collation then planner.c might do the wrong thing in
1195  * higher-level queries.
1196  *
1197  * Note we use RelabelType, not CollateExpr, since this expression
1198  * will reach the executor without any further processing.
1199  */
1200  if (exprCollation(expr) != colColl)
1201  {
1202  expr = (Node *) makeRelabelType((Expr *) expr,
1203  exprType(expr),
1204  exprTypmod(expr),
1205  colColl,
1207  }
1208 
1209  tle = makeTargetEntry((Expr *) expr,
1210  (AttrNumber) resno++,
1211  pstrdup(reftle->resname),
1212  false);
1213 
1214  /*
1215  * By convention, all non-resjunk columns in a setop tree have
1216  * ressortgroupref equal to their resno. In some cases the ref isn't
1217  * needed, but this is a cleaner way than modifying the tlist later.
1218  */
1219  tle->ressortgroupref = tle->resno;
1220 
1221  tlist = lappend(tlist, tle);
1222  }
1223 
1224  if (flag >= 0)
1225  {
1226  /* Add a resjunk flag column */
1227  /* flag value is the given constant */
1228  expr = (Node *) makeConst(INT4OID,
1229  -1,
1230  InvalidOid,
1231  sizeof(int32),
1233  false,
1234  true);
1235  tle = makeTargetEntry((Expr *) expr,
1236  (AttrNumber) resno++,
1237  pstrdup("flag"),
1238  true);
1239  tlist = lappend(tlist, tle);
1240  }
1241 
1242  return tlist;
1243 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
int32 exprTypmod(const Node *expr)
Definition: nodeFuncs.c:276
char * pstrdup(const char *in)
Definition: mcxt.c:1186
Definition: nodes.h:525
unsigned int Oid
Definition: postgres_ext.h:31
char * resname
Definition: primnodes.h:1395
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:297
signed int int32
Definition: c.h:346
bool resjunk
Definition: primnodes.h:1400
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:400
char * flag(int b)
Definition: test-ctype.c:33
AttrNumber resno
Definition: primnodes.h:1394
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:236
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
Node * coerce_to_common_type(ParseState *pstate, Node *node, Oid targetTypeId, const char *context)
List * lappend(List *list, void *datum)
Definition: list.c:322
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
Expr * expr
Definition: primnodes.h:1393
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:720
#define forfour(cell1, list1, cell2, list2, cell3, list3, cell4, list4)
Definition: pg_list.h:476
#define Int32GetDatum(X)
Definition: postgres.h:479
Index ressortgroupref
Definition: primnodes.h:1396
Definition: pg_list.h:50
int16 AttrNumber
Definition: attnum.h:21
#define lfirst_oid(lc)
Definition: pg_list.h:192

◆ generate_union_paths()

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

Definition at line 539 of file prepunion.c.

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

Referenced by recurse_set_operations().

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

◆ make_union_unique()

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

Definition at line 935 of file prepunion.c.

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().

937 {
938  RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
939  List *groupList;
940  double dNumGroups;
941 
942  /* Identify the grouping semantics */
943  groupList = generate_setop_grouplist(op, tlist);
944 
945  /*
946  * XXX for the moment, take the number of distinct groups as equal to the
947  * total input size, ie, the worst case. This is too conservative, but we
948  * don't want to risk having the hashtable overrun memory; also, it's not
949  * clear how to get a decent estimate of the true size. One should note
950  * as well the propensity of novices to write UNION rather than UNION ALL
951  * even when they don't expect any duplicates...
952  */
953  dNumGroups = path->rows;
954 
955  /* Decide whether to hash or sort */
956  if (choose_hashed_setop(root, groupList, path,
957  dNumGroups, dNumGroups,
958  "UNION"))
959  {
960  /* Hashed aggregate plan --- no sort needed */
961  path = (Path *) create_agg_path(root,
962  result_rel,
963  path,
964  create_pathtarget(root, tlist),
965  AGG_HASHED,
967  groupList,
968  NIL,
969  NULL,
970  dNumGroups);
971  }
972  else
973  {
974  /* Sort and Unique */
975  if (groupList)
976  path = (Path *)
977  create_sort_path(root,
978  result_rel,
979  path,
981  groupList,
982  tlist),
983  -1.0);
984  path = (Path *) create_upper_unique_path(root,
985  result_rel,
986  path,
987  list_length(path->pathkeys),
988  dNumGroups);
989  }
990 
991  return path;
992 }
#define NIL
Definition: pg_list.h:65
List * make_pathkeys_for_sortclauses(PlannerInfo *root, List *sortclauses, List *tlist)
Definition: pathkeys.c:1071
UpperUniquePath * create_upper_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, int numCols, double numGroups)
Definition: pathnode.c:2870
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1391
static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses, Path *input_path, double dNumGroups, double dNumOutputRows, const char *construct)
Definition: prepunion.c:1016
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1164
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:2922
#define create_pathtarget(root, tlist)
Definition: tlist.h:54
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2767
List * pathkeys
Definition: pathnodes.h:1128
double rows
Definition: pathnodes.h:1124
static int list_length(const List *l)
Definition: pg_list.h:169
Definition: pg_list.h:50

◆ plan_set_operations()

RelOptInfo* plan_set_operations ( PlannerInfo root)

Definition at line 103 of file prepunion.c.

References Assert, castNode, Query::distinctClause, PlannerInfo::ec_merging_done, PlannerInfo::eq_classes, FromExpr::fromlist, generate_recursion_path(), Query::groupClause, PlannerInfo::hasRecursion, Query::havingQual, IsA, Query::jointree, SetOperationStmt::larg, NIL, parse(), PlannerInfo::parse, PlannerInfo::processed_tlist, FromExpr::quals, recurse_set_operations(), Query::setOperations, setup_simple_rel_arrays(), PlannerInfo::simple_rte_array, RangeTblEntry::subquery, Query::targetList, and Query::windowClause.

Referenced by grouping_planner().

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

◆ plan_union_children()

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

Definition at line 876 of file prepunion.c.

References SetOperationStmt::all, SetOperationStmt::colCollations, SetOperationStmt::colTypes, 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().

880 {
881  List *pending_rels = list_make1(top_union);
882  List *result = NIL;
883  List *child_tlist;
884 
885  *tlist_list = NIL;
886 
887  while (pending_rels != NIL)
888  {
889  Node *setOp = linitial(pending_rels);
890 
891  pending_rels = list_delete_first(pending_rels);
892 
893  if (IsA(setOp, SetOperationStmt))
894  {
895  SetOperationStmt *op = (SetOperationStmt *) setOp;
896 
897  if (op->op == top_union->op &&
898  (op->all == top_union->all || op->all) &&
899  equal(op->colTypes, top_union->colTypes))
900  {
901  /* Same UNION, so fold children into parent */
902  pending_rels = lcons(op->rarg, pending_rels);
903  pending_rels = lcons(op->larg, pending_rels);
904  continue;
905  }
906  }
907 
908  /*
909  * Not same, so plan this child separately.
910  *
911  * Note we disallow any resjunk columns in child results. This is
912  * necessary since the Append node that implements the union won't do
913  * any projection, and upper levels will get confused if some of our
914  * output tuples have junk and some don't. This case only arises when
915  * we have an EXCEPT or INTERSECT as child, else there won't be
916  * resjunk anyway.
917  */
918  result = lappend(result, recurse_set_operations(setOp, root,
919  top_union->colTypes,
920  top_union->colCollations,
921  false, -1,
922  refnames_tlist,
923  &child_tlist,
924  NULL));
925  *tlist_list = lappend(*tlist_list, child_tlist);
926  }
927 
928  return result;
929 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3008
Definition: nodes.h:525
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:207
#define list_make1(x1)
Definition: pg_list.h:227
#define linitial(l)
Definition: pg_list.h:195
List * lappend(List *list, void *datum)
Definition: list.c:322
List * colCollations
Definition: parsenodes.h:1643
List * lcons(void *datum, List *list)
Definition: list.c:454
SetOperation op
Definition: parsenodes.h:1634
Definition: pg_list.h:50
List * list_delete_first(List *list)
Definition: list.c:861

◆ postprocess_setop_rel()

static void postprocess_setop_rel ( PlannerInfo root,
RelOptInfo rel 
)
static

Definition at line 998 of file prepunion.c.

References create_upper_paths_hook, set_cheapest(), and UPPERREL_SETOP.

Referenced by generate_recursion_path(), and recurse_set_operations().

999 {
1000  /*
1001  * We don't currently worry about allowing FDWs to contribute paths to
1002  * this relation, but give extensions a chance.
1003  */
1005  (*create_upper_paths_hook) (root, UPPERREL_SETOP,
1006  NULL, rel, NULL);
1007 
1008  /* Select cheapest path */
1009  set_cheapest(rel);
1010 }
create_upper_paths_hook_type create_upper_paths_hook
Definition: planner.c:78
void set_cheapest(RelOptInfo *parent_rel)
Definition: pathnode.c:245

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

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(), generate_nonunion_paths(), generate_setop_tlist(), generate_union_paths(), get_cheapest_fractional_path(), get_tlist_exprs(), PlannerInfo::glob, Query::groupClause, Query::groupingSets, Query::hasAggs, PlannerInfo::hasHavingQual, IsA, RelOptInfo::lateral_relids, lfirst, linitial, NIL, nodeTag, SetOperationStmt::op, Path::param_info, Path::parent, RelOptInfo::partial_pathlist, RelOptInfo::pathlist, PlannerInfo::plan_params, postprocess_setop_rel(), PlannerInfo::processed_tlist, RelOptInfo::reltarget, RelOptInfo::rows, Path::rows, RangeTblRef::rtindex, set_subquery_size_estimates(), SETOP_UNION, PlannerInfo::simple_rte_array, 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().

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