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

References AGG_HASHED, compare_fractional_path_costs(), cost_agg(), cost_group(), cost_sort(), enable_hashagg, ereport, errcode(), errdetail(), errmsg(), ERROR, get_hash_mem(), 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().

1019 {
1020  int numGroupCols = list_length(groupClauses);
1021  int hash_mem = get_hash_mem();
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  * hash_mem.
1054  */
1055  hashentrysize = MAXALIGN(input_path->pathtarget->width) + MAXALIGN(SizeofMinimalTupleHeader);
1056 
1057  if (hashentrysize * dNumGroups > hash_mem * 1024L)
1058  return false;
1059 
1060  /*
1061  * See if the estimated cost is no more than doing it the other way. We
1062  * deliberately give the hash case more memory when hash_mem exceeds
1063  * standard work mem (i.e. when hash_mem_multiplier exceeds 1.0).
1064  *
1065  * We need to consider input_plan + hashagg versus input_plan + sort +
1066  * group. Note that the actual result plan might involve a SetOp or
1067  * Unique node, not Agg or Group, but the cost estimates for Agg and Group
1068  * should be close enough for our purposes here.
1069  *
1070  * These path variables are dummies that just hold cost fields; we don't
1071  * make actual Paths for these steps.
1072  */
1073  cost_agg(&hashed_p, root, AGG_HASHED, NULL,
1074  numGroupCols, dNumGroups,
1075  NIL,
1076  input_path->startup_cost, input_path->total_cost,
1077  input_path->rows, input_path->pathtarget->width);
1078 
1079  /*
1080  * Now for the sorted case. Note that the input is *always* unsorted,
1081  * since it was made by appending unrelated sub-relations together.
1082  */
1083  sorted_p.startup_cost = input_path->startup_cost;
1084  sorted_p.total_cost = input_path->total_cost;
1085  /* XXX cost_sort doesn't actually look at pathkeys, so just pass NIL */
1086  cost_sort(&sorted_p, root, NIL, sorted_p.total_cost,
1087  input_path->rows, input_path->pathtarget->width,
1088  0.0, work_mem, -1.0);
1089  cost_group(&sorted_p, root, numGroupCols, dNumGroups,
1090  NIL,
1091  sorted_p.startup_cost, sorted_p.total_cost,
1092  input_path->rows);
1093 
1094  /*
1095  * Now make the decision using the top-level tuple fraction. First we
1096  * have to convert an absolute count (LIMIT) into fractional form.
1097  */
1098  tuple_fraction = root->tuple_fraction;
1099  if (tuple_fraction >= 1.0)
1100  tuple_fraction /= dNumOutputRows;
1101 
1102  if (compare_fractional_path_costs(&hashed_p, &sorted_p,
1103  tuple_fraction) < 0)
1104  {
1105  /* Hashed is cheaper, so use it */
1106  return true;
1107  }
1108  return false;
1109 }
#define NIL
Definition: pg_list.h:65
PathTarget * pathtarget
Definition: pathnodes.h:1145
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:2310
int errcode(int sqlerrcode)
Definition: elog.c:610
bool grouping_is_hashable(List *groupClause)
Definition: tlist.c:582
double tuple_fraction
Definition: pathnodes.h:336
#define ERROR
Definition: elog.h:43
Cost startup_cost
Definition: pathnodes.h:1155
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:2568
int errdetail(const char *fmt,...)
Definition: elog.c:957
#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:1927
int work_mem
Definition: globals.c:121
#define ereport(elevel,...)
Definition: elog.h:144
Cost total_cost
Definition: pathnodes.h:1156
double rows
Definition: pathnodes.h:1154
int compare_fractional_path_costs(Path *path1, Path *path2, double fraction)
Definition: pathnode.c:117
size_t Size
Definition: c.h:473
static int list_length(const List *l)
Definition: pg_list.h:169
#define MAXALIGN(LEN)
Definition: c.h:698
bool enable_hashagg
Definition: costsize.c:132
int errmsg(const char *fmt,...)
Definition: elog.c:824
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:562
int get_hash_mem(void)
Definition: nodeHash.c:3389

◆ generate_append_tlist()

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

Definition at line 1265 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().

1269 {
1270  List *tlist = NIL;
1271  int resno = 1;
1272  ListCell *curColType;
1273  ListCell *curColCollation;
1274  ListCell *ref_tl_item;
1275  int colindex;
1276  TargetEntry *tle;
1277  Node *expr;
1278  ListCell *tlistl;
1279  int32 *colTypmods;
1280 
1281  /*
1282  * First extract typmods to use.
1283  *
1284  * If the inputs all agree on type and typmod of a particular column, use
1285  * that typmod; else use -1.
1286  */
1287  colTypmods = (int32 *) palloc(list_length(colTypes) * sizeof(int32));
1288 
1289  foreach(tlistl, input_tlists)
1290  {
1291  List *subtlist = (List *) lfirst(tlistl);
1292  ListCell *subtlistl;
1293 
1294  curColType = list_head(colTypes);
1295  colindex = 0;
1296  foreach(subtlistl, subtlist)
1297  {
1298  TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
1299 
1300  if (subtle->resjunk)
1301  continue;
1302  Assert(curColType != NULL);
1303  if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1304  {
1305  /* If first subplan, copy the typmod; else compare */
1306  int32 subtypmod = exprTypmod((Node *) subtle->expr);
1307 
1308  if (tlistl == list_head(input_tlists))
1309  colTypmods[colindex] = subtypmod;
1310  else if (subtypmod != colTypmods[colindex])
1311  colTypmods[colindex] = -1;
1312  }
1313  else
1314  {
1315  /* types disagree, so force typmod to -1 */
1316  colTypmods[colindex] = -1;
1317  }
1318  curColType = lnext(colTypes, curColType);
1319  colindex++;
1320  }
1321  Assert(curColType == NULL);
1322  }
1323 
1324  /*
1325  * Now we can build the tlist for the Append.
1326  */
1327  colindex = 0;
1328  forthree(curColType, colTypes, curColCollation, colCollations,
1329  ref_tl_item, refnames_tlist)
1330  {
1331  Oid colType = lfirst_oid(curColType);
1332  int32 colTypmod = colTypmods[colindex++];
1333  Oid colColl = lfirst_oid(curColCollation);
1334  TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
1335 
1336  Assert(reftle->resno == resno);
1337  Assert(!reftle->resjunk);
1338  expr = (Node *) makeVar(0,
1339  resno,
1340  colType,
1341  colTypmod,
1342  colColl,
1343  0);
1344  tle = makeTargetEntry((Expr *) expr,
1345  (AttrNumber) resno++,
1346  pstrdup(reftle->resname),
1347  false);
1348 
1349  /*
1350  * By convention, all non-resjunk columns in a setop tree have
1351  * ressortgroupref equal to their resno. In some cases the ref isn't
1352  * needed, but this is a cleaner way than modifying the tlist later.
1353  */
1354  tle->ressortgroupref = tle->resno;
1355 
1356  tlist = lappend(tlist, tle);
1357  }
1358 
1359  if (flag)
1360  {
1361  /* Add a resjunk flag column */
1362  /* flag value is shown as copied up from subplan */
1363  expr = (Node *) makeVar(0,
1364  resno,
1365  INT4OID,
1366  -1,
1367  InvalidOid,
1368  0);
1369  tle = makeTargetEntry((Expr *) expr,
1370  (AttrNumber) resno++,
1371  pstrdup("flag"),
1372  true);
1373  tlist = lappend(tlist, tle);
1374  }
1375 
1376  pfree(colTypmods);
1377 
1378  return tlist;
1379 }
#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:275
#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:529
unsigned int Oid
Definition: postgres_ext.h:31
char * resname
Definition: primnodes.h:1409
signed int int32
Definition: c.h:362
void pfree(void *pointer)
Definition: mcxt.c:1056
bool resjunk
Definition: primnodes.h:1414
char * flag(int b)
Definition: test-ctype.c:33
AttrNumber resno
Definition: primnodes.h:1408
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:238
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:321
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
Expr * expr
Definition: primnodes.h:1407
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
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:1410
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:1265
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
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:3367
List * make_pathkeys_for_sortclauses(PlannerInfo *root, List *sortclauses, List *tlist)
Definition: pathkeys.c:1125
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1393
#define Min(x, y)
Definition: c.h:927
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:1015
double tuple_fraction
Definition: pathnodes.h:336
#define ERROR
Definition: elog.h:43
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1192
struct Path * cheapest_total_path
Definition: pathnodes.h:683
#define create_pathtarget(root, tlist)
Definition: tlist.h:54
Relids relids
Definition: pathnodes.h:665
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:1215
List * colCollations
Definition: parsenodes.h:1667
double rows
Definition: pathnodes.h:668
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2851
double rows
Definition: pathnodes.h:1154
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:1658
SetOpCmd
Definition: nodes.h:802
#define elog(elevel,...)
Definition: elog.h:214
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:676

◆ 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:1265
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
Definition: prepunion.c:997
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1393
int errcode(int sqlerrcode)
Definition: elog.c:610
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:353
#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:3429
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1192
struct Path * cheapest_total_path
Definition: pathnodes.h:683
int errdetail(const char *fmt,...)
Definition: elog.c:957
#define create_pathtarget(root, tlist)
Definition: tlist.h:54
Relids relids
Definition: pathnodes.h:665
List * colCollations
Definition: parsenodes.h:1667
#define ereport(elevel,...)
Definition: elog.h:144
#define Assert(condition)
Definition: c.h:745
double rows
Definition: pathnodes.h:1154
struct Path * non_recursive_path
Definition: pathnodes.h:354
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:225
SetOperation op
Definition: parsenodes.h:1658
int errmsg(const char *fmt,...)
Definition: elog.c:824
#define elog(elevel,...)
Definition: elog.h:214
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:676

◆ generate_setop_grouplist()

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

Definition at line 1393 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().

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

1129 {
1130  List *tlist = NIL;
1131  int resno = 1;
1132  ListCell *ctlc,
1133  *cclc,
1134  *itlc,
1135  *rtlc;
1136  TargetEntry *tle;
1137  Node *expr;
1138 
1139  forfour(ctlc, colTypes, cclc, colCollations,
1140  itlc, input_tlist, rtlc, refnames_tlist)
1141  {
1142  Oid colType = lfirst_oid(ctlc);
1143  Oid colColl = lfirst_oid(cclc);
1144  TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
1145  TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
1146 
1147  Assert(inputtle->resno == resno);
1148  Assert(reftle->resno == resno);
1149  Assert(!inputtle->resjunk);
1150  Assert(!reftle->resjunk);
1151 
1152  /*
1153  * Generate columns referencing input columns and having appropriate
1154  * data types and column names. Insert datatype coercions where
1155  * necessary.
1156  *
1157  * HACK: constants in the input's targetlist are copied up as-is
1158  * rather than being referenced as subquery outputs. This is mainly
1159  * to ensure that when we try to coerce them to the output column's
1160  * datatype, the right things happen for UNKNOWN constants. But do
1161  * this only at the first level of subquery-scan plans; we don't want
1162  * phony constants appearing in the output tlists of upper-level
1163  * nodes!
1164  */
1165  if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
1166  expr = (Node *) inputtle->expr;
1167  else
1168  expr = (Node *) makeVar(varno,
1169  inputtle->resno,
1170  exprType((Node *) inputtle->expr),
1171  exprTypmod((Node *) inputtle->expr),
1172  exprCollation((Node *) inputtle->expr),
1173  0);
1174 
1175  if (exprType(expr) != colType)
1176  {
1177  /*
1178  * Note: it's not really cool to be applying coerce_to_common_type
1179  * here; one notable point is that assign_expr_collations never
1180  * gets run on any generated nodes. For the moment that's not a
1181  * problem because we force the correct exposed collation below.
1182  * It would likely be best to make the parser generate the correct
1183  * output tlist for every set-op to begin with, though.
1184  */
1185  expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */
1186  expr,
1187  colType,
1188  "UNION/INTERSECT/EXCEPT");
1189  }
1190 
1191  /*
1192  * Ensure the tlist entry's exposed collation matches the set-op. This
1193  * is necessary because plan_set_operations() reports the result
1194  * ordering as a list of SortGroupClauses, which don't carry collation
1195  * themselves but just refer to tlist entries. If we don't show the
1196  * right collation then planner.c might do the wrong thing in
1197  * higher-level queries.
1198  *
1199  * Note we use RelabelType, not CollateExpr, since this expression
1200  * will reach the executor without any further processing.
1201  */
1202  if (exprCollation(expr) != colColl)
1203  {
1204  expr = (Node *) makeRelabelType((Expr *) expr,
1205  exprType(expr),
1206  exprTypmod(expr),
1207  colColl,
1209  }
1210 
1211  tle = makeTargetEntry((Expr *) expr,
1212  (AttrNumber) resno++,
1213  pstrdup(reftle->resname),
1214  false);
1215 
1216  /*
1217  * By convention, all non-resjunk columns in a setop tree have
1218  * ressortgroupref equal to their resno. In some cases the ref isn't
1219  * needed, but this is a cleaner way than modifying the tlist later.
1220  */
1221  tle->ressortgroupref = tle->resno;
1222 
1223  tlist = lappend(tlist, tle);
1224  }
1225 
1226  if (flag >= 0)
1227  {
1228  /* Add a resjunk flag column */
1229  /* flag value is the given constant */
1230  expr = (Node *) makeConst(INT4OID,
1231  -1,
1232  InvalidOid,
1233  sizeof(int32),
1235  false,
1236  true);
1237  tle = makeTargetEntry((Expr *) expr,
1238  (AttrNumber) resno++,
1239  pstrdup("flag"),
1240  true);
1241  tlist = lappend(tlist, tle);
1242  }
1243 
1244  return tlist;
1245 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
int32 exprTypmod(const Node *expr)
Definition: nodeFuncs.c:275
char * pstrdup(const char *in)
Definition: mcxt.c:1186
Definition: nodes.h:529
unsigned int Oid
Definition: postgres_ext.h:31
char * resname
Definition: primnodes.h:1409
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:299
signed int int32
Definition: c.h:362
bool resjunk
Definition: primnodes.h:1414
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:402
char * flag(int b)
Definition: test-ctype.c:33
AttrNumber resno
Definition: primnodes.h:1408
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:238
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:321
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
Expr * expr
Definition: primnodes.h:1407
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:719
#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:1410
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:1265
GatherPath * create_gather_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target, Relids required_outer, double *rows)
Definition: pathnode.c:1878
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
#define Min(x, y)
Definition: c.h:927
int parallel_workers
Definition: pathnodes.h:1151
static Path * make_union_unique(SetOperationStmt *op, Path *path, List *tlist, PlannerInfo *root)
Definition: prepunion.c:935
List * partial_pathlist
Definition: pathnodes.h:681
bool enable_parallel_append
Definition: costsize.c:140
int fls(int mask)
Definition: fls.c:55
double tuple_fraction
Definition: pathnodes.h:336
#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:1192
struct Path * cheapest_total_path
Definition: pathnodes.h:683
#define create_pathtarget(root, tlist)
Definition: tlist.h:54
Relids relids
Definition: pathnodes.h:665
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:1215
List * lappend(List *list, void *datum)
Definition: list.c:321
List * colCollations
Definition: parsenodes.h:1667
double rows
Definition: pathnodes.h:668
#define Max(x, y)
Definition: c.h:921
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
double rows
Definition: pathnodes.h:1154
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:673
int max_parallel_workers_per_gather
Definition: costsize.c:123
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:676

◆ 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
948  * it's not clear how to get a decent estimate of the true size. One
949  * should note as well the propensity of novices to write UNION rather
950  * than UNION ALL even when they don't expect any duplicates...
951  */
952  dNumGroups = path->rows;
953 
954  /* Decide whether to hash or sort */
955  if (choose_hashed_setop(root, groupList, path,
956  dNumGroups, dNumGroups,
957  "UNION"))
958  {
959  /* Hashed aggregate plan --- no sort needed */
960  path = (Path *) create_agg_path(root,
961  result_rel,
962  path,
963  create_pathtarget(root, tlist),
964  AGG_HASHED,
966  groupList,
967  NIL,
968  NULL,
969  dNumGroups);
970  }
971  else
972  {
973  /* Sort and Unique */
974  if (groupList)
975  path = (Path *)
976  create_sort_path(root,
977  result_rel,
978  path,
980  groupList,
981  tlist),
982  -1.0);
983  path = (Path *) create_upper_unique_path(root,
984  result_rel,
985  path,
986  list_length(path->pathkeys),
987  dNumGroups);
988  }
989 
990  return path;
991 }
#define NIL
Definition: pg_list.h:65
List * make_pathkeys_for_sortclauses(PlannerInfo *root, List *sortclauses, List *tlist)
Definition: pathkeys.c:1125
UpperUniquePath * create_upper_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, int numCols, double numGroups)
Definition: pathnode.c:2954
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1393
static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses, Path *input_path, double dNumGroups, double dNumOutputRows, const char *construct)
Definition: prepunion.c:1015
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1192
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:3006
#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:2851
List * pathkeys
Definition: pathnodes.h:1158
double rows
Definition: pathnodes.h:1154
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:580
Query * parse
Definition: pathnodes.h:179
bool ec_merging_done
Definition: pathnodes.h:268
FromExpr * jointree
Definition: parsenodes.h:138
#define castNode(_type_, nodeptr)
Definition: nodes.h:598
Definition: nodes.h:529
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:1510
bool hasRecursion
Definition: pathnodes.h:350
Node * quals
Definition: primnodes.h:1511
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:211
#define Assert(condition)
Definition: c.h:745
List * eq_classes
Definition: pathnodes.h:266
void setup_simple_rel_arrays(PlannerInfo *root)
Definition: relnode.c:83
Node * setOperations
Definition: parsenodes.h:166
Query * subquery
Definition: parsenodes.h:1011
List * groupClause
Definition: parsenodes.h:148
Node * havingQual
Definition: parsenodes.h:152
List * processed_tlist
Definition: pathnodes.h:325
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:580
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3033
Definition: nodes.h:529
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:321
List * colCollations
Definition: parsenodes.h:1667
List * lcons(void *datum, List *list)
Definition: list.c:453
SetOperation op
Definition: parsenodes.h:1658
Definition: pg_list.h:50
List * list_delete_first(List *list)
Definition: list.c:860

◆ postprocess_setop_rel()

static void postprocess_setop_rel ( PlannerInfo root,
RelOptInfo rel 
)
static

Definition at line 997 of file prepunion.c.

References create_upper_paths_hook, set_cheapest(), and UPPERREL_SETOP.

Referenced by generate_recursion_path(), and recurse_set_operations().

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

◆ 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:2644
void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition: costsize.c:5136
#define NIL
Definition: pg_list.h:65
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset)
Definition: selfuncs.c:3360
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
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:422
SubqueryScanPath * create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, Relids required_outer)
Definition: pathnode.c:1917
List * plan_params
Definition: pathnodes.h:193
static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
Definition: prepunion.c:997
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:1147
List * groupingSets
Definition: parsenodes.h:150
ProjectionPath * create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target)
Definition: pathnode.c:2553
List * partial_pathlist
Definition: pathnodes.h:681
List * targetList
Definition: parsenodes.h:140
PlannerInfo * subroot
Definition: pathnodes.h:709
Relids lateral_relids
Definition: pathnodes.h:690
double tuple_fraction
Definition: pathnodes.h:336
#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:1144
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1192
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:3312
PlannerGlobal * glob
Definition: pathnodes.h:181
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:194
RangeTblEntry ** simple_rte_array
Definition: pathnodes.h:211
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:668
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
double rows
Definition: pathnodes.h:1154
SetOperation op
Definition: parsenodes.h:1658
bool consider_parallel
Definition: pathnodes.h:673
#define nodeTag(nodeptr)
Definition: nodes.h:534
Query * subquery
Definition: parsenodes.h:1011
List * groupClause
Definition: parsenodes.h:148
void add_partial_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:749
#define elog(elevel,...)
Definition: elog.h:214
bool hasHavingQual
Definition: pathnodes.h:347
List * pathlist
Definition: pathnodes.h:679
bool tlist_same_collations(List *tlist, List *colCollations, bool junkOK)
Definition: tlist.c:304
List * processed_tlist
Definition: pathnodes.h:325
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:676
PlannerInfo * subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, bool hasRecursion, double tuple_fraction)
Definition: planner.c:597
Datum subpath(PG_FUNCTION_ARGS)
Definition: ltree_op.c:241
Path * get_cheapest_fractional_path(RelOptInfo *rel, double tuple_fraction)
Definition: planner.c:5992
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:1123