PostgreSQL Source Code  git master
prepunion.c File Reference
#include "postgres.h"
#include <limits.h>
#include "access/heapam.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 dependency graph for prepunion.c:

Go to the source code of this file.

Data Structures

struct  adjust_appendrel_attrs_context
 

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)
 
static void expand_inherited_rtentry (PlannerInfo *root, RangeTblEntry *rte, Index rti)
 
static void expand_partitioned_rtentry (PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, LOCKMODE lockmode, List **appinfos)
 
static void expand_single_inheritance_child (PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, Relation childrel, List **appinfos, RangeTblEntry **childrte_p, Index *childRTindex_p)
 
static void make_inh_translation_list (Relation oldrelation, Relation newrelation, Index newvarno, List **translated_vars)
 
static Bitmapsettranslate_col_privs (const Bitmapset *parent_privs, List *translated_vars)
 
static Nodeadjust_appendrel_attrs_mutator (Node *node, adjust_appendrel_attrs_context *context)
 
static Relids adjust_child_relids (Relids relids, int nappinfos, AppendRelInfo **appinfos)
 
static Listadjust_inherited_tlist (List *tlist, AppendRelInfo *context)
 
RelOptInfoplan_set_operations (PlannerInfo *root)
 
void expand_inherited_tables (PlannerInfo *root)
 
Nodeadjust_appendrel_attrs (PlannerInfo *root, Node *node, int nappinfos, AppendRelInfo **appinfos)
 
Relids adjust_child_relids_multilevel (PlannerInfo *root, Relids relids, Relids child_relids, Relids top_parent_relids)
 
Nodeadjust_appendrel_attrs_multilevel (PlannerInfo *root, Node *node, Relids child_relids, Relids top_parent_relids)
 
SpecialJoinInfobuild_child_join_sjinfo (PlannerInfo *root, SpecialJoinInfo *parent_sjinfo, Relids left_relids, Relids right_relids)
 
AppendRelInfo ** find_appinfos_by_relids (PlannerInfo *root, Relids relids, int *nappinfos)
 

Function Documentation

◆ adjust_appendrel_attrs()

Node* adjust_appendrel_attrs ( PlannerInfo root,
Node node,
int  nappinfos,
AppendRelInfo **  appinfos 
)

Definition at line 2047 of file prepunion.c.

References adjust_appendrel_attrs_mutator(), adjust_inherited_tlist(), adjust_appendrel_attrs_context::appinfos, Assert, AppendRelInfo::child_relid, CMD_UPDATE, Query::commandType, IsA, adjust_appendrel_attrs_context::nappinfos, AppendRelInfo::parent_relid, QTW_IGNORE_RC_SUBQUERIES, query_tree_mutator(), Query::resultRelation, adjust_appendrel_attrs_context::root, and Query::targetList.

Referenced by add_child_rel_equivalences(), add_placeholders_to_child_joinrel(), adjust_appendrel_attrs_multilevel(), apply_scanjoin_target_to_paths(), build_child_join_rel(), build_child_join_sjinfo(), create_partitionwise_grouping_paths(), inheritance_planner(), set_append_rel_size(), and try_partitionwise_join().

2049 {
2050  Node *result;
2052 
2053  context.root = root;
2054  context.nappinfos = nappinfos;
2055  context.appinfos = appinfos;
2056 
2057  /* If there's nothing to adjust, don't call this function. */
2058  Assert(nappinfos >= 1 && appinfos != NULL);
2059 
2060  /*
2061  * Must be prepared to start with a Query or a bare expression tree.
2062  */
2063  if (node && IsA(node, Query))
2064  {
2065  Query *newnode;
2066  int cnt;
2067 
2068  newnode = query_tree_mutator((Query *) node,
2070  (void *) &context,
2072  for (cnt = 0; cnt < nappinfos; cnt++)
2073  {
2074  AppendRelInfo *appinfo = appinfos[cnt];
2075 
2076  if (newnode->resultRelation == appinfo->parent_relid)
2077  {
2078  newnode->resultRelation = appinfo->child_relid;
2079  /* Fix tlist resnos too, if it's inherited UPDATE */
2080  if (newnode->commandType == CMD_UPDATE)
2081  newnode->targetList =
2083  appinfo);
2084  break;
2085  }
2086  }
2087 
2088  result = (Node *) newnode;
2089  }
2090  else
2091  result = adjust_appendrel_attrs_mutator(node, &context);
2092 
2093  return result;
2094 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
int resultRelation
Definition: parsenodes.h:122
Definition: nodes.h:517
static List * adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
Definition: prepunion.c:2446
List * targetList
Definition: parsenodes.h:140
#define QTW_IGNORE_RC_SUBQUERIES
Definition: nodeFuncs.h:22
CmdType commandType
Definition: parsenodes.h:112
static Node * adjust_appendrel_attrs_mutator(Node *node, adjust_appendrel_attrs_context *context)
Definition: prepunion.c:2097
#define Assert(condition)
Definition: c.h:699
AppendRelInfo ** appinfos
Definition: prepunion.c:60
Index child_relid
Definition: relation.h:2125
Index parent_relid
Definition: relation.h:2124
Query * query_tree_mutator(Query *query, Node *(*mutator)(), void *context, int flags)
Definition: nodeFuncs.c:3093

◆ adjust_appendrel_attrs_multilevel()

Node* adjust_appendrel_attrs_multilevel ( PlannerInfo root,
Node node,
Relids  child_relids,
Relids  top_parent_relids 
)

Definition at line 2534 of file prepunion.c.

References adjust_appendrel_attrs(), adjust_appendrel_attrs_multilevel(), Assert, bms_add_member(), bms_equal(), bms_num_members(), find_appinfos_by_relids(), AppendRelInfo::parent_relid, and pfree().

Referenced by adjust_appendrel_attrs_multilevel(), generate_join_implied_equalities_broken(), and make_partition_pruneinfo().

2537 {
2538  AppendRelInfo **appinfos;
2539  Bitmapset *parent_relids = NULL;
2540  int nappinfos;
2541  int cnt;
2542 
2543  Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids));
2544 
2545  appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
2546 
2547  /* Construct relids set for the immediate parent of given child. */
2548  for (cnt = 0; cnt < nappinfos; cnt++)
2549  {
2550  AppendRelInfo *appinfo = appinfos[cnt];
2551 
2552  parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
2553  }
2554 
2555  /* Recurse if immediate parent is not the top parent. */
2556  if (!bms_equal(parent_relids, top_parent_relids))
2557  node = adjust_appendrel_attrs_multilevel(root, node, parent_relids,
2558  top_parent_relids);
2559 
2560  /* Now translate for this child */
2561  node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);
2562 
2563  pfree(appinfos);
2564 
2565  return node;
2566 }
Node * adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node, Relids child_relids, Relids top_parent_relids)
Definition: prepunion.c:2534
void pfree(void *pointer)
Definition: mcxt.c:1031
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:671
Node * adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos, AppendRelInfo **appinfos)
Definition: prepunion.c:2047
AppendRelInfo ** find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
Definition: prepunion.c:2618
#define Assert(condition)
Definition: c.h:699
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:764
Index parent_relid
Definition: relation.h:2124
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:153

◆ adjust_appendrel_attrs_mutator()

static Node * adjust_appendrel_attrs_mutator ( Node node,
adjust_appendrel_attrs_context context 
)
static

Definition at line 2097 of file prepunion.c.

References adjust_child_relids(), adjust_appendrel_attrs_context::appinfos, ConvertRowtypeExpr::arg, RowExpr::args, Assert, AppendRelInfo::child_relid, AppendRelInfo::child_reltype, RestrictInfo::clause, RestrictInfo::clause_relids, COERCE_IMPLICIT_CAST, Alias::colnames, RowExpr::colnames, ConvertRowtypeExpr::convertformat, copyObject, CurrentOfExpr::cvarno, elog, RangeTblEntry::eref, ERROR, RestrictInfo::eval_cost, expression_tree_mutator(), get_rel_name(), IsA, RestrictInfo::left_bucketsize, RestrictInfo::left_em, RestrictInfo::left_mcvfreq, RestrictInfo::left_relids, list_length(), list_nth(), ConvertRowtypeExpr::location, RowExpr::location, makeNode, adjust_appendrel_attrs_context::nappinfos, NIL, RestrictInfo::norm_selec, RestrictInfo::nullable_relids, OidIsValid, RestrictInfo::orclause, RestrictInfo::outer_relids, RestrictInfo::outer_selec, AppendRelInfo::parent_relid, AppendRelInfo::parent_reloid, AppendRelInfo::parent_reltype, PlannerInfo::parse, PlaceHolderVar::phlevelsup, PlaceHolderVar::phrels, RestrictInfo::required_relids, ConvertRowtypeExpr::resulttype, RestrictInfo::right_bucketsize, RestrictInfo::right_em, RestrictInfo::right_mcvfreq, RestrictInfo::right_relids, adjust_appendrel_attrs_context::root, RowExpr::row_format, RowExpr::row_typeid, rt_fetch, Query::rtable, RangeTblRef::rtindex, JoinExpr::rtindex, RestrictInfo::scansel_cache, QualCost::startup, AppendRelInfo::translated_vars, Var::varattno, Var::varlevelsup, Var::varno, Var::varnoold, and Var::vartype.

Referenced by adjust_appendrel_attrs().

2099 {
2100  AppendRelInfo **appinfos = context->appinfos;
2101  int nappinfos = context->nappinfos;
2102  int cnt;
2103 
2104  if (node == NULL)
2105  return NULL;
2106  if (IsA(node, Var))
2107  {
2108  Var *var = (Var *) copyObject(node);
2109  AppendRelInfo *appinfo = NULL;
2110 
2111  for (cnt = 0; cnt < nappinfos; cnt++)
2112  {
2113  if (var->varno == appinfos[cnt]->parent_relid)
2114  {
2115  appinfo = appinfos[cnt];
2116  break;
2117  }
2118  }
2119 
2120  if (var->varlevelsup == 0 && appinfo)
2121  {
2122  var->varno = appinfo->child_relid;
2123  var->varnoold = appinfo->child_relid;
2124  if (var->varattno > 0)
2125  {
2126  Node *newnode;
2127 
2128  if (var->varattno > list_length(appinfo->translated_vars))
2129  elog(ERROR, "attribute %d of relation \"%s\" does not exist",
2130  var->varattno, get_rel_name(appinfo->parent_reloid));
2131  newnode = copyObject(list_nth(appinfo->translated_vars,
2132  var->varattno - 1));
2133  if (newnode == NULL)
2134  elog(ERROR, "attribute %d of relation \"%s\" does not exist",
2135  var->varattno, get_rel_name(appinfo->parent_reloid));
2136  return newnode;
2137  }
2138  else if (var->varattno == 0)
2139  {
2140  /*
2141  * Whole-row Var: if we are dealing with named rowtypes, we
2142  * can use a whole-row Var for the child table plus a coercion
2143  * step to convert the tuple layout to the parent's rowtype.
2144  * Otherwise we have to generate a RowExpr.
2145  */
2146  if (OidIsValid(appinfo->child_reltype))
2147  {
2148  Assert(var->vartype == appinfo->parent_reltype);
2149  if (appinfo->parent_reltype != appinfo->child_reltype)
2150  {
2152 
2153  r->arg = (Expr *) var;
2154  r->resulttype = appinfo->parent_reltype;
2156  r->location = -1;
2157  /* Make sure the Var node has the right type ID, too */
2158  var->vartype = appinfo->child_reltype;
2159  return (Node *) r;
2160  }
2161  }
2162  else
2163  {
2164  /*
2165  * Build a RowExpr containing the translated variables.
2166  *
2167  * In practice var->vartype will always be RECORDOID here,
2168  * so we need to come up with some suitable column names.
2169  * We use the parent RTE's column names.
2170  *
2171  * Note: we can't get here for inheritance cases, so there
2172  * is no need to worry that translated_vars might contain
2173  * some dummy NULLs.
2174  */
2175  RowExpr *rowexpr;
2176  List *fields;
2177  RangeTblEntry *rte;
2178 
2179  rte = rt_fetch(appinfo->parent_relid,
2180  context->root->parse->rtable);
2181  fields = copyObject(appinfo->translated_vars);
2182  rowexpr = makeNode(RowExpr);
2183  rowexpr->args = fields;
2184  rowexpr->row_typeid = var->vartype;
2185  rowexpr->row_format = COERCE_IMPLICIT_CAST;
2186  rowexpr->colnames = copyObject(rte->eref->colnames);
2187  rowexpr->location = -1;
2188 
2189  return (Node *) rowexpr;
2190  }
2191  }
2192  /* system attributes don't need any other translation */
2193  }
2194  return (Node *) var;
2195  }
2196  if (IsA(node, CurrentOfExpr))
2197  {
2198  CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
2199 
2200  for (cnt = 0; cnt < nappinfos; cnt++)
2201  {
2202  AppendRelInfo *appinfo = appinfos[cnt];
2203 
2204  if (cexpr->cvarno == appinfo->parent_relid)
2205  {
2206  cexpr->cvarno = appinfo->child_relid;
2207  break;
2208  }
2209  }
2210  return (Node *) cexpr;
2211  }
2212  if (IsA(node, RangeTblRef))
2213  {
2214  RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
2215 
2216  for (cnt = 0; cnt < nappinfos; cnt++)
2217  {
2218  AppendRelInfo *appinfo = appinfos[cnt];
2219 
2220  if (rtr->rtindex == appinfo->parent_relid)
2221  {
2222  rtr->rtindex = appinfo->child_relid;
2223  break;
2224  }
2225  }
2226  return (Node *) rtr;
2227  }
2228  if (IsA(node, JoinExpr))
2229  {
2230  /* Copy the JoinExpr node with correct mutation of subnodes */
2231  JoinExpr *j;
2232  AppendRelInfo *appinfo;
2233 
2234  j = (JoinExpr *) expression_tree_mutator(node,
2236  (void *) context);
2237  /* now fix JoinExpr's rtindex (probably never happens) */
2238  for (cnt = 0; cnt < nappinfos; cnt++)
2239  {
2240  appinfo = appinfos[cnt];
2241 
2242  if (j->rtindex == appinfo->parent_relid)
2243  {
2244  j->rtindex = appinfo->child_relid;
2245  break;
2246  }
2247  }
2248  return (Node *) j;
2249  }
2250  if (IsA(node, PlaceHolderVar))
2251  {
2252  /* Copy the PlaceHolderVar node with correct mutation of subnodes */
2253  PlaceHolderVar *phv;
2254 
2255  phv = (PlaceHolderVar *) expression_tree_mutator(node,
2257  (void *) context);
2258  /* now fix PlaceHolderVar's relid sets */
2259  if (phv->phlevelsup == 0)
2260  phv->phrels = adjust_child_relids(phv->phrels, context->nappinfos,
2261  context->appinfos);
2262  return (Node *) phv;
2263  }
2264  /* Shouldn't need to handle planner auxiliary nodes here */
2265  Assert(!IsA(node, SpecialJoinInfo));
2266  Assert(!IsA(node, AppendRelInfo));
2267  Assert(!IsA(node, PlaceHolderInfo));
2268  Assert(!IsA(node, MinMaxAggInfo));
2269 
2270  /*
2271  * We have to process RestrictInfo nodes specially. (Note: although
2272  * set_append_rel_pathlist will hide RestrictInfos in the parent's
2273  * baserestrictinfo list from us, it doesn't hide those in joininfo.)
2274  */
2275  if (IsA(node, RestrictInfo))
2276  {
2277  RestrictInfo *oldinfo = (RestrictInfo *) node;
2278  RestrictInfo *newinfo = makeNode(RestrictInfo);
2279 
2280  /* Copy all flat-copiable fields */
2281  memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
2282 
2283  /* Recursively fix the clause itself */
2284  newinfo->clause = (Expr *)
2285  adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);
2286 
2287  /* and the modified version, if an OR clause */
2288  newinfo->orclause = (Expr *)
2289  adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
2290 
2291  /* adjust relid sets too */
2292  newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,
2293  context->nappinfos,
2294  context->appinfos);
2296  context->nappinfos,
2297  context->appinfos);
2298  newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,
2299  context->nappinfos,
2300  context->appinfos);
2302  context->nappinfos,
2303  context->appinfos);
2304  newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,
2305  context->nappinfos,
2306  context->appinfos);
2307  newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,
2308  context->nappinfos,
2309  context->appinfos);
2310 
2311  /*
2312  * Reset cached derivative fields, since these might need to have
2313  * different values when considering the child relation. Note we
2314  * don't reset left_ec/right_ec: each child variable is implicitly
2315  * equivalent to its parent, so still a member of the same EC if any.
2316  */
2317  newinfo->eval_cost.startup = -1;
2318  newinfo->norm_selec = -1;
2319  newinfo->outer_selec = -1;
2320  newinfo->left_em = NULL;
2321  newinfo->right_em = NULL;
2322  newinfo->scansel_cache = NIL;
2323  newinfo->left_bucketsize = -1;
2324  newinfo->right_bucketsize = -1;
2325  newinfo->left_mcvfreq = -1;
2326  newinfo->right_mcvfreq = -1;
2327 
2328  return (Node *) newinfo;
2329  }
2330 
2331  /*
2332  * NOTE: we do not need to recurse into sublinks, because they should
2333  * already have been converted to subplans before we see them.
2334  */
2335  Assert(!IsA(node, SubLink));
2336  Assert(!IsA(node, Query));
2337 
2339  (void *) context);
2340 }
QualCost eval_cost
Definition: relation.h:1917
#define NIL
Definition: pg_list.h:69
List * args
Definition: primnodes.h:991
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
Query * parse
Definition: relation.h:169
Index varlevelsup
Definition: primnodes.h:174
Node * expression_tree_mutator(Node *node, Node *(*mutator)(), void *context)
Definition: nodeFuncs.c:2420
Relids required_relids
Definition: relation.h:1898
List * colnames
Definition: primnodes.h:44
Selectivity right_mcvfreq
Definition: relation.h:1944
Expr * orclause
Definition: relation.h:1911
Relids clause_relids
Definition: relation.h:1895
Definition: nodes.h:517
Relids left_relids
Definition: relation.h:1907
AttrNumber varattno
Definition: primnodes.h:169
Definition: primnodes.h:164
#define OidIsValid(objectId)
Definition: c.h:605
List * translated_vars
Definition: relation.h:2152
Oid parent_reltype
Definition: relation.h:2133
Cost startup
Definition: relation.h:46
Relids outer_relids
Definition: relation.h:1901
Selectivity norm_selec
Definition: relation.h:1918
Index varnoold
Definition: primnodes.h:177
List * rtable
Definition: parsenodes.h:137
Relids phrels
Definition: relation.h:2000
#define ERROR
Definition: elog.h:43
List * colnames
Definition: primnodes.h:1007
Oid vartype
Definition: primnodes.h:171
EquivalenceMember * left_em
Definition: relation.h:1930
int location
Definition: primnodes.h:1008
void * list_nth(const List *list, int n)
Definition: list.c:410
Selectivity outer_selec
Definition: relation.h:1921
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
EquivalenceMember * right_em
Definition: relation.h:1931
Expr * clause
Definition: relation.h:1880
Index varno
Definition: primnodes.h:167
Relids nullable_relids
Definition: relation.h:1904
CoercionForm convertformat
Definition: primnodes.h:867
Selectivity left_bucketsize
Definition: relation.h:1941
#define makeNode(_type_)
Definition: nodes.h:565
Relids right_relids
Definition: relation.h:1908
static Node * adjust_appendrel_attrs_mutator(Node *node, adjust_appendrel_attrs_context *context)
Definition: prepunion.c:2097
#define Assert(condition)
Definition: c.h:699
Selectivity left_mcvfreq
Definition: relation.h:1943
Oid row_typeid
Definition: primnodes.h:992
static int list_length(const List *l)
Definition: pg_list.h:89
AppendRelInfo ** appinfos
Definition: prepunion.c:60
Oid child_reltype
Definition: relation.h:2134
Index phlevelsup
Definition: relation.h:2002
Selectivity right_bucketsize
Definition: relation.h:1942
#define elog
Definition: elog.h:219
Index child_relid
Definition: relation.h:2125
Alias * eref
Definition: parsenodes.h:1066
Oid parent_reloid
Definition: relation.h:2159
#define copyObject(obj)
Definition: nodes.h:630
Index parent_relid
Definition: relation.h:2124
CoercionForm row_format
Definition: primnodes.h:1006
int rtindex
Definition: primnodes.h:1464
Definition: pg_list.h:45
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1730
List * scansel_cache
Definition: relation.h:1932
static Relids adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)
Definition: prepunion.c:2347

◆ adjust_child_relids()

static Relids adjust_child_relids ( Relids  relids,
int  nappinfos,
AppendRelInfo **  appinfos 
)
static

Definition at line 2347 of file prepunion.c.

References bms_add_member(), bms_copy(), bms_del_member(), bms_is_member(), AppendRelInfo::child_relid, and AppendRelInfo::parent_relid.

Referenced by adjust_appendrel_attrs_mutator(), adjust_child_relids_multilevel(), and build_child_join_sjinfo().

2348 {
2349  Bitmapset *result = NULL;
2350  int cnt;
2351 
2352  for (cnt = 0; cnt < nappinfos; cnt++)
2353  {
2354  AppendRelInfo *appinfo = appinfos[cnt];
2355 
2356  /* Remove parent, add child */
2357  if (bms_is_member(appinfo->parent_relid, relids))
2358  {
2359  /* Make a copy if we are changing the set. */
2360  if (!result)
2361  result = bms_copy(relids);
2362 
2363  result = bms_del_member(result, appinfo->parent_relid);
2364  result = bms_add_member(result, appinfo->child_relid);
2365  }
2366  }
2367 
2368  /* If we made any changes, return the modified copy. */
2369  if (result)
2370  return result;
2371 
2372  /* Otherwise, return the original set without modification. */
2373  return relids;
2374 }
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:133
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:764
Index child_relid
Definition: relation.h:2125
Index parent_relid
Definition: relation.h:2124
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:801
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:486

◆ adjust_child_relids_multilevel()

Relids adjust_child_relids_multilevel ( PlannerInfo root,
Relids  relids,
Relids  child_relids,
Relids  top_parent_relids 
)

Definition at line 2382 of file prepunion.c.

References adjust_child_relids(), adjust_child_relids_multilevel(), bms_add_member(), bms_equal(), bms_free(), bms_overlap(), find_appinfos_by_relids(), AppendRelInfo::parent_relid, and pfree().

Referenced by adjust_child_relids_multilevel(), and reparameterize_path_by_child().

2384 {
2385  AppendRelInfo **appinfos;
2386  int nappinfos;
2387  Relids parent_relids = NULL;
2388  Relids result;
2389  Relids tmp_result = NULL;
2390  int cnt;
2391 
2392  /*
2393  * If the given relids set doesn't contain any of the top parent relids,
2394  * it will remain unchanged.
2395  */
2396  if (!bms_overlap(relids, top_parent_relids))
2397  return relids;
2398 
2399  appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
2400 
2401  /* Construct relids set for the immediate parent of the given child. */
2402  for (cnt = 0; cnt < nappinfos; cnt++)
2403  {
2404  AppendRelInfo *appinfo = appinfos[cnt];
2405 
2406  parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
2407  }
2408 
2409  /* Recurse if immediate parent is not the top parent. */
2410  if (!bms_equal(parent_relids, top_parent_relids))
2411  {
2412  tmp_result = adjust_child_relids_multilevel(root, relids,
2413  parent_relids,
2414  top_parent_relids);
2415  relids = tmp_result;
2416  }
2417 
2418  result = adjust_child_relids(relids, nappinfos, appinfos);
2419 
2420  /* Free memory consumed by any intermediate result. */
2421  if (tmp_result)
2422  bms_free(tmp_result);
2423  bms_free(parent_relids);
2424  pfree(appinfos);
2425 
2426  return result;
2427 }
Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids, Relids child_relids, Relids top_parent_relids)
Definition: prepunion.c:2382
void pfree(void *pointer)
Definition: mcxt.c:1031
AppendRelInfo ** find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
Definition: prepunion.c:2618
void bms_free(Bitmapset *a)
Definition: bitmapset.c:267
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:764
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:509
Index parent_relid
Definition: relation.h:2124
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:153
static Relids adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)
Definition: prepunion.c:2347

◆ adjust_inherited_tlist()

static List * adjust_inherited_tlist ( List tlist,
AppendRelInfo context 
)
static

Definition at line 2446 of file prepunion.c.

References Assert, elog, ERROR, get_rel_name(), IsA, lappend(), lfirst, list_length(), list_nth(), NIL, OidIsValid, AppendRelInfo::parent_reloid, TargetEntry::resjunk, TargetEntry::resno, AppendRelInfo::translated_vars, and Var::varattno.

Referenced by adjust_appendrel_attrs().

2447 {
2448  bool changed_it = false;
2449  ListCell *tl;
2450  List *new_tlist;
2451  bool more;
2452  int attrno;
2453 
2454  /* This should only happen for an inheritance case, not UNION ALL */
2455  Assert(OidIsValid(context->parent_reloid));
2456 
2457  /* Scan tlist and update resnos to match attnums of child rel */
2458  foreach(tl, tlist)
2459  {
2460  TargetEntry *tle = (TargetEntry *) lfirst(tl);
2461  Var *childvar;
2462 
2463  if (tle->resjunk)
2464  continue; /* ignore junk items */
2465 
2466  /* Look up the translation of this column: it must be a Var */
2467  if (tle->resno <= 0 ||
2468  tle->resno > list_length(context->translated_vars))
2469  elog(ERROR, "attribute %d of relation \"%s\" does not exist",
2470  tle->resno, get_rel_name(context->parent_reloid));
2471  childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);
2472  if (childvar == NULL || !IsA(childvar, Var))
2473  elog(ERROR, "attribute %d of relation \"%s\" does not exist",
2474  tle->resno, get_rel_name(context->parent_reloid));
2475 
2476  if (tle->resno != childvar->varattno)
2477  {
2478  tle->resno = childvar->varattno;
2479  changed_it = true;
2480  }
2481  }
2482 
2483  /*
2484  * If we changed anything, re-sort the tlist by resno, and make sure
2485  * resjunk entries have resnos above the last real resno. The sort
2486  * algorithm is a bit stupid, but for such a seldom-taken path, small is
2487  * probably better than fast.
2488  */
2489  if (!changed_it)
2490  return tlist;
2491 
2492  new_tlist = NIL;
2493  more = true;
2494  for (attrno = 1; more; attrno++)
2495  {
2496  more = false;
2497  foreach(tl, tlist)
2498  {
2499  TargetEntry *tle = (TargetEntry *) lfirst(tl);
2500 
2501  if (tle->resjunk)
2502  continue; /* ignore junk items */
2503 
2504  if (tle->resno == attrno)
2505  new_tlist = lappend(new_tlist, tle);
2506  else if (tle->resno > attrno)
2507  more = true;
2508  }
2509  }
2510 
2511  foreach(tl, tlist)
2512  {
2513  TargetEntry *tle = (TargetEntry *) lfirst(tl);
2514 
2515  if (!tle->resjunk)
2516  continue; /* here, ignore non-junk items */
2517 
2518  tle->resno = attrno;
2519  new_tlist = lappend(new_tlist, tle);
2520  attrno++;
2521  }
2522 
2523  return new_tlist;
2524 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
AttrNumber varattno
Definition: primnodes.h:169
Definition: primnodes.h:164
#define OidIsValid(objectId)
Definition: c.h:605
List * translated_vars
Definition: relation.h:2152
bool resjunk
Definition: primnodes.h:1383
#define ERROR
Definition: elog.h:43
void * list_nth(const List *list, int n)
Definition: list.c:410
AttrNumber resno
Definition: primnodes.h:1377
List * lappend(List *list, void *datum)
Definition: list.c:128
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
static int list_length(const List *l)
Definition: pg_list.h:89
#define elog
Definition: elog.h:219
Oid parent_reloid
Definition: relation.h:2159
Definition: pg_list.h:45
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1730

◆ build_child_join_sjinfo()

SpecialJoinInfo* build_child_join_sjinfo ( PlannerInfo root,
SpecialJoinInfo parent_sjinfo,
Relids  left_relids,
Relids  right_relids 
)

Definition at line 2574 of file prepunion.c.

References adjust_appendrel_attrs(), adjust_child_relids(), find_appinfos_by_relids(), makeNode, SpecialJoinInfo::min_lefthand, SpecialJoinInfo::min_righthand, pfree(), SpecialJoinInfo::semi_rhs_exprs, SpecialJoinInfo::syn_lefthand, and SpecialJoinInfo::syn_righthand.

Referenced by try_partitionwise_join().

2576 {
2578  AppendRelInfo **left_appinfos;
2579  int left_nappinfos;
2580  AppendRelInfo **right_appinfos;
2581  int right_nappinfos;
2582 
2583  memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));
2584  left_appinfos = find_appinfos_by_relids(root, left_relids,
2585  &left_nappinfos);
2586  right_appinfos = find_appinfos_by_relids(root, right_relids,
2587  &right_nappinfos);
2588 
2589  sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand,
2590  left_nappinfos, left_appinfos);
2592  right_nappinfos,
2593  right_appinfos);
2594  sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand,
2595  left_nappinfos, left_appinfos);
2597  right_nappinfos,
2598  right_appinfos);
2599  sjinfo->semi_rhs_exprs = (List *) adjust_appendrel_attrs(root,
2600  (Node *) sjinfo->semi_rhs_exprs,
2601  right_nappinfos,
2602  right_appinfos);
2603 
2604  pfree(left_appinfos);
2605  pfree(right_appinfos);
2606 
2607  return sjinfo;
2608 }
Relids min_righthand
Definition: relation.h:2067
Definition: nodes.h:517
Relids syn_lefthand
Definition: relation.h:2068
Relids syn_righthand
Definition: relation.h:2069
void pfree(void *pointer)
Definition: mcxt.c:1031
List * semi_rhs_exprs
Definition: relation.h:2077
Node * adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos, AppendRelInfo **appinfos)
Definition: prepunion.c:2047
AppendRelInfo ** find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
Definition: prepunion.c:2618
#define makeNode(_type_)
Definition: nodes.h:565
Definition: pg_list.h:45
Relids min_lefthand
Definition: relation.h:2066
static Relids adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)
Definition: prepunion.c:2347

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

1049 {
1050  int numGroupCols = list_length(groupClauses);
1051  bool can_sort;
1052  bool can_hash;
1053  Size hashentrysize;
1054  Path hashed_p;
1055  Path sorted_p;
1056  double tuple_fraction;
1057 
1058  /* Check whether the operators support sorting or hashing */
1059  can_sort = grouping_is_sortable(groupClauses);
1060  can_hash = grouping_is_hashable(groupClauses);
1061  if (can_hash && can_sort)
1062  {
1063  /* we have a meaningful choice to make, continue ... */
1064  }
1065  else if (can_hash)
1066  return true;
1067  else if (can_sort)
1068  return false;
1069  else
1070  ereport(ERROR,
1071  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1072  /* translator: %s is UNION, INTERSECT, or EXCEPT */
1073  errmsg("could not implement %s", construct),
1074  errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
1075 
1076  /* Prefer sorting when enable_hashagg is off */
1077  if (!enable_hashagg)
1078  return false;
1079 
1080  /*
1081  * Don't do it if it doesn't look like the hashtable will fit into
1082  * work_mem.
1083  */
1084  hashentrysize = MAXALIGN(input_path->pathtarget->width) + MAXALIGN(SizeofMinimalTupleHeader);
1085 
1086  if (hashentrysize * dNumGroups > work_mem * 1024L)
1087  return false;
1088 
1089  /*
1090  * See if the estimated cost is no more than doing it the other way.
1091  *
1092  * We need to consider input_plan + hashagg versus input_plan + sort +
1093  * group. Note that the actual result plan might involve a SetOp or
1094  * Unique node, not Agg or Group, but the cost estimates for Agg and Group
1095  * should be close enough for our purposes here.
1096  *
1097  * These path variables are dummies that just hold cost fields; we don't
1098  * make actual Paths for these steps.
1099  */
1100  cost_agg(&hashed_p, root, AGG_HASHED, NULL,
1101  numGroupCols, dNumGroups,
1102  NIL,
1103  input_path->startup_cost, input_path->total_cost,
1104  input_path->rows);
1105 
1106  /*
1107  * Now for the sorted case. Note that the input is *always* unsorted,
1108  * since it was made by appending unrelated sub-relations together.
1109  */
1110  sorted_p.startup_cost = input_path->startup_cost;
1111  sorted_p.total_cost = input_path->total_cost;
1112  /* XXX cost_sort doesn't actually look at pathkeys, so just pass NIL */
1113  cost_sort(&sorted_p, root, NIL, sorted_p.total_cost,
1114  input_path->rows, input_path->pathtarget->width,
1115  0.0, work_mem, -1.0);
1116  cost_group(&sorted_p, root, numGroupCols, dNumGroups,
1117  NIL,
1118  sorted_p.startup_cost, sorted_p.total_cost,
1119  input_path->rows);
1120 
1121  /*
1122  * Now make the decision using the top-level tuple fraction. First we
1123  * have to convert an absolute count (LIMIT) into fractional form.
1124  */
1125  tuple_fraction = root->tuple_fraction;
1126  if (tuple_fraction >= 1.0)
1127  tuple_fraction /= dNumOutputRows;
1128 
1129  if (compare_fractional_path_costs(&hashed_p, &sorted_p,
1130  tuple_fraction) < 0)
1131  {
1132  /* Hashed is cheaper, so use it */
1133  return true;
1134  }
1135  return false;
1136 }
#define NIL
Definition: pg_list.h:69
PathTarget * pathtarget
Definition: relation.h:1079
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:2060
int errcode(int sqlerrcode)
Definition: elog.c:575
bool grouping_is_hashable(List *groupClause)
Definition: tlist.c:538
double tuple_fraction
Definition: relation.h:306
#define ERROR
Definition: elog.h:43
Cost startup_cost
Definition: relation.h:1089
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:2248
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define ereport(elevel, rest)
Definition: elog.h:122
#define SizeofMinimalTupleHeader
Definition: htup_details.h:662
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:1661
int work_mem
Definition: globals.c:122
Cost total_cost
Definition: relation.h:1090
double rows
Definition: relation.h:1088
int compare_fractional_path_costs(Path *path1, Path *path2, double fraction)
Definition: pathnode.c:117
size_t Size
Definition: c.h:433
static int list_length(const List *l)
Definition: pg_list.h:89
#define MAXALIGN(LEN)
Definition: c.h:652
bool enable_hashagg
Definition: costsize.c:131
int errmsg(const char *fmt,...)
Definition: elog.c:797
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:518

◆ expand_inherited_rtentry()

static void expand_inherited_rtentry ( PlannerInfo root,
RangeTblEntry rte,
Index  rti 
)
static

Definition at line 1509 of file prepunion.c.

References AccessShareLock, PlannerInfo::append_rel_list, Assert, expand_partitioned_rtentry(), expand_single_inheritance_child(), find_all_inheritors(), get_plan_rowmark(), has_subclass(), heap_close, heap_open(), RangeTblEntry::inh, PlanRowMark::isParent, lfirst_oid, list_concat(), list_length(), PlanRowMark::markType, NIL, NoLock, parse(), PlannerInfo::parse, RELATION_IS_OTHER_TEMP, RelationGetPartitionDesc, RangeTblEntry::relid, RangeTblEntry::relkind, Query::resultRelation, RowExclusiveLock, RowMarkRequiresRowShareLock, PlannerInfo::rowMarks, RowShareLock, RTE_RELATION, RTE_SUBQUERY, and RangeTblEntry::rtekind.

Referenced by expand_inherited_tables().

1510 {
1511  Query *parse = root->parse;
1512  Oid parentOID;
1513  PlanRowMark *oldrc;
1514  Relation oldrelation;
1515  LOCKMODE lockmode;
1516  List *inhOIDs;
1517  ListCell *l;
1518 
1519  /* Does RT entry allow inheritance? */
1520  if (!rte->inh)
1521  return;
1522  /* Ignore any already-expanded UNION ALL nodes */
1523  if (rte->rtekind != RTE_RELATION)
1524  {
1525  Assert(rte->rtekind == RTE_SUBQUERY);
1526  return;
1527  }
1528  /* Fast path for common case of childless table */
1529  parentOID = rte->relid;
1530  if (!has_subclass(parentOID))
1531  {
1532  /* Clear flag before returning */
1533  rte->inh = false;
1534  return;
1535  }
1536 
1537  /*
1538  * The rewriter should already have obtained an appropriate lock on each
1539  * relation named in the query. However, for each child relation we add
1540  * to the query, we must obtain an appropriate lock, because this will be
1541  * the first use of those relations in the parse/rewrite/plan pipeline.
1542  *
1543  * If the parent relation is the query's result relation, then we need
1544  * RowExclusiveLock. Otherwise, if it's accessed FOR UPDATE/SHARE, we
1545  * need RowShareLock; otherwise AccessShareLock. We can't just grab
1546  * AccessShareLock because then the executor would be trying to upgrade
1547  * the lock, leading to possible deadlocks. (This code should match the
1548  * parser and rewriter.)
1549  */
1550  oldrc = get_plan_rowmark(root->rowMarks, rti);
1551  if (rti == parse->resultRelation)
1552  lockmode = RowExclusiveLock;
1553  else if (oldrc && RowMarkRequiresRowShareLock(oldrc->markType))
1554  lockmode = RowShareLock;
1555  else
1556  lockmode = AccessShareLock;
1557 
1558  /* Scan for all members of inheritance set, acquire needed locks */
1559  inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
1560 
1561  /*
1562  * Check that there's at least one descendant, else treat as no-child
1563  * case. This could happen despite above has_subclass() check, if table
1564  * once had a child but no longer does.
1565  */
1566  if (list_length(inhOIDs) < 2)
1567  {
1568  /* Clear flag before returning */
1569  rte->inh = false;
1570  return;
1571  }
1572 
1573  /*
1574  * If parent relation is selected FOR UPDATE/SHARE, we need to mark its
1575  * PlanRowMark as isParent = true, and generate a new PlanRowMark for each
1576  * child.
1577  */
1578  if (oldrc)
1579  oldrc->isParent = true;
1580 
1581  /*
1582  * Must open the parent relation to examine its tupdesc. We need not lock
1583  * it; we assume the rewriter already did.
1584  */
1585  oldrelation = heap_open(parentOID, NoLock);
1586 
1587  /* Scan the inheritance set and expand it */
1588  if (RelationGetPartitionDesc(oldrelation) != NULL)
1589  {
1590  Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
1591 
1592  /*
1593  * If this table has partitions, recursively expand them in the order
1594  * in which they appear in the PartitionDesc. While at it, also
1595  * extract the partition key columns of all the partitioned tables.
1596  */
1597  expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
1598  lockmode, &root->append_rel_list);
1599  }
1600  else
1601  {
1602  List *appinfos = NIL;
1603  RangeTblEntry *childrte;
1604  Index childRTindex;
1605 
1606  /*
1607  * This table has no partitions. Expand any plain inheritance
1608  * children in the order the OIDs were returned by
1609  * find_all_inheritors.
1610  */
1611  foreach(l, inhOIDs)
1612  {
1613  Oid childOID = lfirst_oid(l);
1614  Relation newrelation;
1615 
1616  /* Open rel if needed; we already have required locks */
1617  if (childOID != parentOID)
1618  newrelation = heap_open(childOID, NoLock);
1619  else
1620  newrelation = oldrelation;
1621 
1622  /*
1623  * It is possible that the parent table has children that are temp
1624  * tables of other backends. We cannot safely access such tables
1625  * (because of buffering issues), and the best thing to do seems
1626  * to be to silently ignore them.
1627  */
1628  if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
1629  {
1630  heap_close(newrelation, lockmode);
1631  continue;
1632  }
1633 
1634  expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,
1635  newrelation,
1636  &appinfos, &childrte,
1637  &childRTindex);
1638 
1639  /* Close child relations, but keep locks */
1640  if (childOID != parentOID)
1641  heap_close(newrelation, NoLock);
1642  }
1643 
1644  /*
1645  * If all the children were temp tables, pretend it's a
1646  * non-inheritance situation; we don't need Append node in that case.
1647  * The duplicate RTE we added for the parent table is harmless, so we
1648  * don't bother to get rid of it; ditto for the useless PlanRowMark
1649  * node.
1650  */
1651  if (list_length(appinfos) < 2)
1652  rte->inh = false;
1653  else
1655  appinfos);
1656 
1657  }
1658 
1659  heap_close(oldrelation, NoLock);
1660 }
#define NIL
Definition: pg_list.h:69
List * rowMarks
Definition: relation.h:268
Query * parse
Definition: relation.h:169
RowMarkType markType
Definition: plannodes.h:1041
int LOCKMODE
Definition: lockdefs.h:26
int resultRelation
Definition: parsenodes.h:122
#define AccessShareLock
Definition: lockdefs.h:36
List * list_concat(List *list1, List *list2)
Definition: list.c:321
static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, Relation childrel, List **appinfos, RangeTblEntry **childrte_p, Index *childRTindex_p)
Definition: prepunion.c:1767
#define heap_close(r, l)
Definition: heapam.h:97
unsigned int Oid
Definition: postgres_ext.h:31
#define RowMarkRequiresRowShareLock(marktype)
Definition: plannodes.h:994
bool has_subclass(Oid relationId)
Definition: pg_inherits.c:261
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
#define RowShareLock
Definition: lockdefs.h:37
List * append_rel_list
Definition: relation.h:266
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1294
unsigned int Index
Definition: c.h:442
#define Assert(condition)
Definition: c.h:699
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:538
static int list_length(const List *l)
Definition: pg_list.h:89
RTEKind rtekind
Definition: parsenodes.h:962
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:166
PlanRowMark * get_plan_rowmark(List *rowmarks, Index rtindex)
Definition: preptlist.c:424
bool isParent
Definition: plannodes.h:1045
static void expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, LOCKMODE lockmode, List **appinfos)
Definition: prepunion.c:1670
Definition: pg_list.h:45
#define lfirst_oid(lc)
Definition: pg_list.h:108
#define RelationGetPartitionDesc(relation)
Definition: rel.h:595
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:649

◆ expand_inherited_tables()

void expand_inherited_tables ( PlannerInfo root)

Definition at line 1466 of file prepunion.c.

References expand_inherited_rtentry(), lfirst, list_head(), list_length(), lnext, PlannerInfo::parse, and Query::rtable.

Referenced by subquery_planner().

1467 {
1468  Index nrtes;
1469  Index rti;
1470  ListCell *rl;
1471 
1472  /*
1473  * expand_inherited_rtentry may add RTEs to parse->rtable. The function is
1474  * expected to recursively handle any RTEs that it creates with inh=true.
1475  * So just scan as far as the original end of the rtable list.
1476  */
1477  nrtes = list_length(root->parse->rtable);
1478  rl = list_head(root->parse->rtable);
1479  for (rti = 1; rti <= nrtes; rti++)
1480  {
1481  RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
1482 
1483  expand_inherited_rtentry(root, rte, rti);
1484  rl = lnext(rl);
1485  }
1486 }
Query * parse
Definition: relation.h:169
List * rtable
Definition: parsenodes.h:137
static ListCell * list_head(const List *l)
Definition: pg_list.h:77
#define lnext(lc)
Definition: pg_list.h:105
static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
Definition: prepunion.c:1509
unsigned int Index
Definition: c.h:442
#define lfirst(lc)
Definition: pg_list.h:106
static int list_length(const List *l)
Definition: pg_list.h:89

◆ expand_partitioned_rtentry()

static void expand_partitioned_rtentry ( PlannerInfo root,
RangeTblEntry parentrte,
Index  parentRTindex,
Relation  parentrel,
PlanRowMark top_parentrc,
LOCKMODE  lockmode,
List **  appinfos 
)
static

Definition at line 1670 of file prepunion.c.

References Assert, check_stack_depth(), expand_single_inheritance_child(), has_partition_attrs(), heap_close, heap_open(), i, RangeTblEntry::inh, NoLock, PartitionDescData::nparts, PartitionDescData::oids, PlannerInfo::partColsUpdated, RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetPartitionDesc, and RangeTblEntry::updatedCols.

Referenced by expand_inherited_rtentry().

1674 {
1675  int i;
1676  RangeTblEntry *childrte;
1677  Index childRTindex;
1678  bool has_child = false;
1679  PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);
1680 
1682 
1683  /* A partitioned table should always have a partition descriptor. */
1684  Assert(partdesc);
1685 
1686  Assert(parentrte->inh);
1687 
1688  /*
1689  * Note down whether any partition key cols are being updated. Though it's
1690  * the root partitioned table's updatedCols we are interested in, we
1691  * instead use parentrte to get the updatedCols. This is convenient
1692  * because parentrte already has the root partrel's updatedCols translated
1693  * to match the attribute ordering of parentrel.
1694  */
1695  if (!root->partColsUpdated)
1696  root->partColsUpdated =
1697  has_partition_attrs(parentrel, parentrte->updatedCols, NULL);
1698 
1699  /* First expand the partitioned table itself. */
1700  expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,
1701  top_parentrc, parentrel,
1702  appinfos, &childrte, &childRTindex);
1703 
1704  for (i = 0; i < partdesc->nparts; i++)
1705  {
1706  Oid childOID = partdesc->oids[i];
1707  Relation childrel;
1708 
1709  /* Open rel; we already have required locks */
1710  childrel = heap_open(childOID, NoLock);
1711 
1712  /* As in expand_inherited_rtentry, skip non-local temp tables */
1713  if (RELATION_IS_OTHER_TEMP(childrel))
1714  {
1715  heap_close(childrel, lockmode);
1716  continue;
1717  }
1718 
1719  /* We have a real partition. */
1720  has_child = true;
1721 
1722  expand_single_inheritance_child(root, parentrte, parentRTindex,
1723  parentrel, top_parentrc, childrel,
1724  appinfos, &childrte, &childRTindex);
1725 
1726  /* If this child is itself partitioned, recurse */
1727  if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1728  expand_partitioned_rtentry(root, childrte, childRTindex,
1729  childrel, top_parentrc, lockmode,
1730  appinfos);
1731 
1732  /* Close child relation, but keep locks */
1733  heap_close(childrel, NoLock);
1734  }
1735 
1736  /*
1737  * If the partitioned table has no partitions or all the partitions are
1738  * temporary tables from other backends, treat this as non-inheritance
1739  * case.
1740  */
1741  if (!has_child)
1742  parentrte->inh = false;
1743 }
static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, Relation childrel, List **appinfos, RangeTblEntry **childrte_p, Index *childRTindex_p)
Definition: prepunion.c:1767
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:84
unsigned int Oid
Definition: postgres_ext.h:31
bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
Definition: partition.c:206
#define NoLock
Definition: lockdefs.h:34
void check_stack_depth(void)
Definition: postgres.c:3159
bool partColsUpdated
Definition: relation.h:335
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1294
unsigned int Index
Definition: c.h:442
Bitmapset * updatedCols
Definition: parsenodes.h:1074
#define Assert(condition)
Definition: c.h:699
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:538
int i
static void expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, LOCKMODE lockmode, List **appinfos)
Definition: prepunion.c:1670
#define RelationGetPartitionDesc(relation)
Definition: rel.h:595

◆ expand_single_inheritance_child()

static void expand_single_inheritance_child ( PlannerInfo root,
RangeTblEntry parentrte,
Index  parentRTindex,
Relation  parentrel,
PlanRowMark top_parentrc,
Relation  childrel,
List **  appinfos,
RangeTblEntry **  childrte_p,
Index childRTindex_p 
)
static

Definition at line 1767 of file prepunion.c.

References PlanRowMark::allMarkTypes, AppendRelInfo::child_relid, AppendRelInfo::child_reltype, copyObject, RangeTblEntry::inh, RangeTblEntry::insertedCols, PlanRowMark::isParent, lappend(), list_length(), make_inh_translation_list(), makeNode, PlanRowMark::markType, NIL, AppendRelInfo::parent_relid, AppendRelInfo::parent_reloid, AppendRelInfo::parent_reltype, parse(), PlannerInfo::parse, PlanRowMark::prti, RelationData::rd_rel, RelationGetRelid, RangeTblEntry::relid, RangeTblEntry::relkind, RangeTblEntry::requiredPerms, PlanRowMark::rowmarkId, PlannerInfo::rowMarks, Query::rtable, PlanRowMark::rti, RangeTblEntry::securityQuals, select_rowmark_type(), RangeTblEntry::selectedCols, PlanRowMark::strength, translate_col_privs(), AppendRelInfo::translated_vars, RangeTblEntry::updatedCols, and PlanRowMark::waitPolicy.

Referenced by expand_inherited_rtentry(), and expand_partitioned_rtentry().

1772 {
1773  Query *parse = root->parse;
1774  Oid parentOID = RelationGetRelid(parentrel);
1775  Oid childOID = RelationGetRelid(childrel);
1776  RangeTblEntry *childrte;
1777  Index childRTindex;
1778  AppendRelInfo *appinfo;
1779 
1780  /*
1781  * Build an RTE for the child, and attach to query's rangetable list. We
1782  * copy most fields of the parent's RTE, but replace relation OID and
1783  * relkind, and set inh = false. Also, set requiredPerms to zero since
1784  * all required permissions checks are done on the original RTE. Likewise,
1785  * set the child's securityQuals to empty, because we only want to apply
1786  * the parent's RLS conditions regardless of what RLS properties
1787  * individual children may have. (This is an intentional choice to make
1788  * inherited RLS work like regular permissions checks.) The parent
1789  * securityQuals will be propagated to children along with other base
1790  * restriction clauses, so we don't need to do it here.
1791  */
1792  childrte = copyObject(parentrte);
1793  *childrte_p = childrte;
1794  childrte->relid = childOID;
1795  childrte->relkind = childrel->rd_rel->relkind;
1796  /* A partitioned child will need to be expanded further. */
1797  if (childOID != parentOID &&
1798  childrte->relkind == RELKIND_PARTITIONED_TABLE)
1799  childrte->inh = true;
1800  else
1801  childrte->inh = false;
1802  childrte->requiredPerms = 0;
1803  childrte->securityQuals = NIL;
1804  parse->rtable = lappend(parse->rtable, childrte);
1805  childRTindex = list_length(parse->rtable);
1806  *childRTindex_p = childRTindex;
1807 
1808  /*
1809  * We need an AppendRelInfo if paths will be built for the child RTE. If
1810  * childrte->inh is true, then we'll always need to generate append paths
1811  * for it. If childrte->inh is false, we must scan it if it's not a
1812  * partitioned table; but if it is a partitioned table, then it never has
1813  * any data of its own and need not be scanned.
1814  */
1815  if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
1816  {
1817  appinfo = makeNode(AppendRelInfo);
1818  appinfo->parent_relid = parentRTindex;
1819  appinfo->child_relid = childRTindex;
1820  appinfo->parent_reltype = parentrel->rd_rel->reltype;
1821  appinfo->child_reltype = childrel->rd_rel->reltype;
1822  make_inh_translation_list(parentrel, childrel, childRTindex,
1823  &appinfo->translated_vars);
1824  appinfo->parent_reloid = parentOID;
1825  *appinfos = lappend(*appinfos, appinfo);
1826 
1827  /*
1828  * Translate the column permissions bitmaps to the child's attnums (we
1829  * have to build the translated_vars list before we can do this). But
1830  * if this is the parent table, leave copyObject's result alone.
1831  *
1832  * Note: we need to do this even though the executor won't run any
1833  * permissions checks on the child RTE. The insertedCols/updatedCols
1834  * bitmaps may be examined for trigger-firing purposes.
1835  */
1836  if (childOID != parentOID)
1837  {
1838  childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
1839  appinfo->translated_vars);
1840  childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
1841  appinfo->translated_vars);
1842  childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
1843  appinfo->translated_vars);
1844  }
1845  }
1846 
1847  /*
1848  * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
1849  */
1850  if (top_parentrc)
1851  {
1852  PlanRowMark *childrc = makeNode(PlanRowMark);
1853 
1854  childrc->rti = childRTindex;
1855  childrc->prti = top_parentrc->rti;
1856  childrc->rowmarkId = top_parentrc->rowmarkId;
1857  /* Reselect rowmark type, because relkind might not match parent */
1858  childrc->markType = select_rowmark_type(childrte,
1859  top_parentrc->strength);
1860  childrc->allMarkTypes = (1 << childrc->markType);
1861  childrc->strength = top_parentrc->strength;
1862  childrc->waitPolicy = top_parentrc->waitPolicy;
1863 
1864  /*
1865  * We mark RowMarks for partitioned child tables as parent RowMarks so
1866  * that the executor ignores them (except their existence means that
1867  * the child tables be locked using appropriate mode).
1868  */
1869  childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
1870 
1871  /* Include child's rowmark type in top parent's allMarkTypes */
1872  top_parentrc->allMarkTypes |= childrc->allMarkTypes;
1873 
1874  root->rowMarks = lappend(root->rowMarks, childrc);
1875  }
1876 }
#define NIL
Definition: pg_list.h:69
List * rowMarks
Definition: relation.h:268
Query * parse
Definition: relation.h:169
RowMarkType markType
Definition: plannodes.h:1041
RowMarkType select_rowmark_type(RangeTblEntry *rte, LockClauseStrength strength)
Definition: planner.c:2613
List * securityQuals
Definition: parsenodes.h:1075
Index prti
Definition: plannodes.h:1039
static Bitmapset * translate_col_privs(const Bitmapset *parent_privs, List *translated_vars)
Definition: prepunion.c:1993
AclMode requiredPerms
Definition: parsenodes.h:1070
Form_pg_class rd_rel
Definition: rel.h:84
unsigned int Oid
Definition: postgres_ext.h:31
Index rowmarkId
Definition: plannodes.h:1040
LockWaitPolicy waitPolicy
Definition: plannodes.h:1044
List * translated_vars
Definition: relation.h:2152
Oid parent_reltype
Definition: relation.h:2133
Bitmapset * selectedCols
Definition: parsenodes.h:1072
List * rtable
Definition: parsenodes.h:137
int allMarkTypes
Definition: plannodes.h:1042
List * lappend(List *list, void *datum)
Definition: list.c:128
unsigned int Index
Definition: c.h:442
Bitmapset * updatedCols
Definition: parsenodes.h:1074
#define makeNode(_type_)
Definition: nodes.h:565
LockClauseStrength strength
Definition: plannodes.h:1043
static int list_length(const List *l)
Definition: pg_list.h:89
Oid child_reltype
Definition: relation.h:2134
static void make_inh_translation_list(Relation oldrelation, Relation newrelation, Index newvarno, List **translated_vars)
Definition: prepunion.c:1886
Bitmapset * insertedCols
Definition: parsenodes.h:1073
bool isParent
Definition: plannodes.h:1045
Index child_relid
Definition: relation.h:2125
Oid parent_reloid
Definition: relation.h:2159
#define copyObject(obj)
Definition: nodes.h:630
Index parent_relid
Definition: relation.h:2124
#define RelationGetRelid(relation)
Definition: rel.h:407
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:649

◆ find_appinfos_by_relids()

AppendRelInfo** find_appinfos_by_relids ( PlannerInfo root,
Relids  relids,
int *  nappinfos 
)

Definition at line 2618 of file prepunion.c.

References PlannerInfo::append_rel_list, bms_is_member(), bms_num_members(), AppendRelInfo::child_relid, elog, ERROR, lfirst, and palloc().

Referenced by add_placeholders_to_child_joinrel(), adjust_appendrel_attrs_multilevel(), adjust_child_relids_multilevel(), apply_scanjoin_target_to_paths(), build_child_join_rel(), build_child_join_sjinfo(), create_partitionwise_grouping_paths(), and try_partitionwise_join().

2619 {
2620  ListCell *lc;
2621  AppendRelInfo **appinfos;
2622  int cnt = 0;
2623 
2624  *nappinfos = bms_num_members(relids);
2625  appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos);
2626 
2627  foreach(lc, root->append_rel_list)
2628  {
2629  AppendRelInfo *appinfo = lfirst(lc);
2630 
2631  if (bms_is_member(appinfo->child_relid, relids))
2632  {
2633  appinfos[cnt] = appinfo;
2634  cnt++;
2635 
2636  /* Stop when we have gathered all the AppendRelInfos. */
2637  if (cnt == *nappinfos)
2638  return appinfos;
2639  }
2640  }
2641 
2642  /* Should have found the entries ... */
2643  elog(ERROR, "did not find all requested child rels in append_rel_list");
2644  return NULL; /* not reached */
2645 }
#define ERROR
Definition: elog.h:43
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:671
List * append_rel_list
Definition: relation.h:266
#define lfirst(lc)
Definition: pg_list.h:106
void * palloc(Size size)
Definition: mcxt.c:924
#define elog
Definition: elog.h:219
Index child_relid
Definition: relation.h:2125
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:486

◆ generate_append_tlist()

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

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

1299 {
1300  List *tlist = NIL;
1301  int resno = 1;
1302  ListCell *curColType;
1303  ListCell *curColCollation;
1304  ListCell *ref_tl_item;
1305  int colindex;
1306  TargetEntry *tle;
1307  Node *expr;
1308  ListCell *tlistl;
1309  int32 *colTypmods;
1310 
1311  /*
1312  * First extract typmods to use.
1313  *
1314  * If the inputs all agree on type and typmod of a particular column, use
1315  * that typmod; else use -1.
1316  */
1317  colTypmods = (int32 *) palloc(list_length(colTypes) * sizeof(int32));
1318 
1319  foreach(tlistl, input_tlists)
1320  {
1321  List *subtlist = (List *) lfirst(tlistl);
1322  ListCell *subtlistl;
1323 
1324  curColType = list_head(colTypes);
1325  colindex = 0;
1326  foreach(subtlistl, subtlist)
1327  {
1328  TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
1329 
1330  if (subtle->resjunk)
1331  continue;
1332  Assert(curColType != NULL);
1333  if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1334  {
1335  /* If first subplan, copy the typmod; else compare */
1336  int32 subtypmod = exprTypmod((Node *) subtle->expr);
1337 
1338  if (tlistl == list_head(input_tlists))
1339  colTypmods[colindex] = subtypmod;
1340  else if (subtypmod != colTypmods[colindex])
1341  colTypmods[colindex] = -1;
1342  }
1343  else
1344  {
1345  /* types disagree, so force typmod to -1 */
1346  colTypmods[colindex] = -1;
1347  }
1348  curColType = lnext(curColType);
1349  colindex++;
1350  }
1351  Assert(curColType == NULL);
1352  }
1353 
1354  /*
1355  * Now we can build the tlist for the Append.
1356  */
1357  colindex = 0;
1358  forthree(curColType, colTypes, curColCollation, colCollations,
1359  ref_tl_item, refnames_tlist)
1360  {
1361  Oid colType = lfirst_oid(curColType);
1362  int32 colTypmod = colTypmods[colindex++];
1363  Oid colColl = lfirst_oid(curColCollation);
1364  TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
1365 
1366  Assert(reftle->resno == resno);
1367  Assert(!reftle->resjunk);
1368  expr = (Node *) makeVar(0,
1369  resno,
1370  colType,
1371  colTypmod,
1372  colColl,
1373  0);
1374  tle = makeTargetEntry((Expr *) expr,
1375  (AttrNumber) resno++,
1376  pstrdup(reftle->resname),
1377  false);
1378 
1379  /*
1380  * By convention, all non-resjunk columns in a setop tree have
1381  * ressortgroupref equal to their resno. In some cases the ref isn't
1382  * needed, but this is a cleaner way than modifying the tlist later.
1383  */
1384  tle->ressortgroupref = tle->resno;
1385 
1386  tlist = lappend(tlist, tle);
1387  }
1388 
1389  if (flag)
1390  {
1391  /* Add a resjunk flag column */
1392  /* flag value is shown as copied up from subplan */
1393  expr = (Node *) makeVar(0,
1394  resno,
1395  INT4OID,
1396  -1,
1397  InvalidOid,
1398  0);
1399  tle = makeTargetEntry((Expr *) expr,
1400  (AttrNumber) resno++,
1401  pstrdup("flag"),
1402  true);
1403  tlist = lappend(tlist, tle);
1404  }
1405 
1406  pfree(colTypmods);
1407 
1408  return tlist;
1409 }
#define NIL
Definition: pg_list.h:69
int32 exprTypmod(const Node *expr)
Definition: nodeFuncs.c:276
#define forthree(cell1, list1, cell2, list2, cell3, list3)
Definition: pg_list.h:203
char * pstrdup(const char *in)
Definition: mcxt.c:1161
Definition: nodes.h:517
unsigned int Oid
Definition: postgres_ext.h:31
char * resname
Definition: primnodes.h:1378
signed int int32
Definition: c.h:313
void pfree(void *pointer)
Definition: mcxt.c:1031
bool resjunk
Definition: primnodes.h:1383
char * flag(int b)
Definition: test-ctype.c:33
AttrNumber resno
Definition: primnodes.h:1377
static ListCell * list_head(const List *l)
Definition: pg_list.h:77
#define lnext(lc)
Definition: pg_list.h:105
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:237
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
List * lappend(List *list, void *datum)
Definition: list.c:128
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
Expr * expr
Definition: primnodes.h:1376
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
static int list_length(const List *l)
Definition: pg_list.h:89
void * palloc(Size size)
Definition: mcxt.c:924
Index ressortgroupref
Definition: primnodes.h:1379
Definition: pg_list.h:45
int16 AttrNumber
Definition: attnum.h:21
#define lfirst_oid(lc)
Definition: pg_list.h:108

◆ generate_nonunion_paths()

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

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

731 {
732  RelOptInfo *result_rel;
733  RelOptInfo *lrel,
734  *rrel;
735  double save_fraction = root->tuple_fraction;
736  Path *lpath,
737  *rpath,
738  *path;
739  List *lpath_tlist,
740  *rpath_tlist,
741  *tlist_list,
742  *tlist,
743  *groupList,
744  *pathlist;
745  double dLeftGroups,
746  dRightGroups,
747  dNumGroups,
748  dNumOutputRows;
749  bool use_hash;
750  SetOpCmd cmd;
751  int firstFlag;
752 
753  /*
754  * Tell children to fetch all tuples.
755  */
756  root->tuple_fraction = 0.0;
757 
758  /* Recurse on children, ensuring their outputs are marked */
759  lrel = recurse_set_operations(op->larg, root,
760  op->colTypes, op->colCollations,
761  false, 0,
762  refnames_tlist,
763  &lpath_tlist,
764  &dLeftGroups);
765  lpath = lrel->cheapest_total_path;
766  rrel = recurse_set_operations(op->rarg, root,
767  op->colTypes, op->colCollations,
768  false, 1,
769  refnames_tlist,
770  &rpath_tlist,
771  &dRightGroups);
772  rpath = rrel->cheapest_total_path;
773 
774  /* Undo effects of forcing tuple_fraction to 0 */
775  root->tuple_fraction = save_fraction;
776 
777  /*
778  * For EXCEPT, we must put the left input first. For INTERSECT, either
779  * order should give the same results, and we prefer to put the smaller
780  * input first in order to minimize the size of the hash table in the
781  * hashing case. "Smaller" means the one with the fewer groups.
782  */
783  if (op->op == SETOP_EXCEPT || dLeftGroups <= dRightGroups)
784  {
785  pathlist = list_make2(lpath, rpath);
786  tlist_list = list_make2(lpath_tlist, rpath_tlist);
787  firstFlag = 0;
788  }
789  else
790  {
791  pathlist = list_make2(rpath, lpath);
792  tlist_list = list_make2(rpath_tlist, lpath_tlist);
793  firstFlag = 1;
794  }
795 
796  /*
797  * Generate tlist for Append plan node.
798  *
799  * The tlist for an Append plan isn't important as far as the Append is
800  * concerned, but we must make it look real anyway for the benefit of the
801  * next plan level up. In fact, it has to be real enough that the flag
802  * column is shown as a variable not a constant, else setrefs.c will get
803  * confused.
804  */
805  tlist = generate_append_tlist(op->colTypes, op->colCollations, true,
806  tlist_list, refnames_tlist);
807 
808  *pTargetList = tlist;
809 
810  /* Build result relation. */
811  result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
812  bms_union(lrel->relids, rrel->relids));
813  result_rel->reltarget = create_pathtarget(root, tlist);;
814 
815  /*
816  * Append the child results together.
817  */
818  path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
819  NULL, 0, false, NIL, -1);
820 
821  /* Identify the grouping semantics */
822  groupList = generate_setop_grouplist(op, tlist);
823 
824  /*
825  * Estimate number of distinct groups that we'll need hashtable entries
826  * for; this is the size of the left-hand input for EXCEPT, or the smaller
827  * input for INTERSECT. Also estimate the number of eventual output rows.
828  * In non-ALL cases, we estimate each group produces one output row; in
829  * ALL cases use the relevant relation size. These are worst-case
830  * estimates, of course, but we need to be conservative.
831  */
832  if (op->op == SETOP_EXCEPT)
833  {
834  dNumGroups = dLeftGroups;
835  dNumOutputRows = op->all ? lpath->rows : dNumGroups;
836  }
837  else
838  {
839  dNumGroups = Min(dLeftGroups, dRightGroups);
840  dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
841  }
842 
843  /*
844  * Decide whether to hash or sort, and add a sort node if needed.
845  */
846  use_hash = choose_hashed_setop(root, groupList, path,
847  dNumGroups, dNumOutputRows,
848  (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT");
849 
850  if (groupList && !use_hash)
851  path = (Path *) create_sort_path(root,
852  result_rel,
853  path,
855  groupList,
856  tlist),
857  -1.0);
858 
859  /*
860  * Finally, add a SetOp path node to generate the correct output.
861  */
862  switch (op->op)
863  {
864  case SETOP_INTERSECT:
866  break;
867  case SETOP_EXCEPT:
869  break;
870  default:
871  elog(ERROR, "unrecognized set op: %d", (int) op->op);
872  cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */
873  break;
874  }
875  path = (Path *) create_setop_path(root,
876  result_rel,
877  path,
878  cmd,
879  use_hash ? SETOP_HASHED : SETOP_SORTED,
880  groupList,
881  list_length(op->colTypes) + 1,
882  use_hash ? firstFlag : -1,
883  dNumGroups,
884  dNumOutputRows);
885 
886  result_rel->rows = path->rows;
887  add_path(result_rel, path);
888  return result_rel;
889 }
#define list_make2(x1, x2)
Definition: pg_list.h:140
#define NIL
Definition: pg_list.h:69
static List * generate_append_tlist(List *colTypes, List *colCollations, bool flag, List *input_tlists, List *refnames_tlist)
Definition: prepunion.c:1295
AppendPath * create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *partial_subpaths, Relids required_outer, int parallel_workers, bool parallel_aware, List *partitioned_rels, double rows)
Definition: pathnode.c:1219
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:3145
List * make_pathkeys_for_sortclauses(PlannerInfo *root, List *sortclauses, List *tlist)
Definition: pathkeys.c:874
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1423
#define Min(x, y)
Definition: c.h:857
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:237
static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses, Path *input_path, double dNumGroups, double dNumOutputRows, const char *construct)
Definition: prepunion.c:1045
double tuple_fraction
Definition: relation.h:306
#define ERROR
Definition: elog.h:43
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1145
struct Path * cheapest_total_path
Definition: relation.h:630
#define create_pathtarget(root, tlist)
Definition: tlist.h:69
Relids relids
Definition: relation.h:612
List * colCollations
Definition: parsenodes.h:1608
double rows
Definition: relation.h:615
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2631
double rows
Definition: relation.h:1088
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:284
static int list_length(const List *l)
Definition: pg_list.h:89
SetOperation op
Definition: parsenodes.h:1599
SetOpCmd
Definition: nodes.h:787
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
struct PathTarget * reltarget
Definition: relation.h:623

◆ generate_recursion_path()

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

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

466 {
467  RelOptInfo *result_rel;
468  Path *path;
469  RelOptInfo *lrel,
470  *rrel;
471  Path *lpath;
472  Path *rpath;
473  List *lpath_tlist;
474  List *rpath_tlist;
475  List *tlist;
476  List *groupList;
477  double dNumGroups;
478 
479  /* Parser should have rejected other cases */
480  if (setOp->op != SETOP_UNION)
481  elog(ERROR, "only UNION queries can be recursive");
482  /* Worktable ID should be assigned */
483  Assert(root->wt_param_id >= 0);
484 
485  /*
486  * Unlike a regular UNION node, process the left and right inputs
487  * separately without any intention of combining them into one Append.
488  */
489  lrel = recurse_set_operations(setOp->larg, root,
490  setOp->colTypes, setOp->colCollations,
491  false, -1,
492  refnames_tlist,
493  &lpath_tlist,
494  NULL);
495  lpath = lrel->cheapest_total_path;
496  /* The right path will want to look at the left one ... */
497  root->non_recursive_path = lpath;
498  rrel = recurse_set_operations(setOp->rarg, root,
499  setOp->colTypes, setOp->colCollations,
500  false, -1,
501  refnames_tlist,
502  &rpath_tlist,
503  NULL);
504  rpath = rrel->cheapest_total_path;
505  root->non_recursive_path = NULL;
506 
507  /*
508  * Generate tlist for RecursiveUnion path node --- same as in Append cases
509  */
510  tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations, false,
511  list_make2(lpath_tlist, rpath_tlist),
512  refnames_tlist);
513 
514  *pTargetList = tlist;
515 
516  /* Build result relation. */
517  result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
518  bms_union(lrel->relids, rrel->relids));
519  result_rel->reltarget = create_pathtarget(root, tlist);
520 
521  /*
522  * If UNION, identify the grouping operators
523  */
524  if (setOp->all)
525  {
526  groupList = NIL;
527  dNumGroups = 0;
528  }
529  else
530  {
531  /* Identify the grouping semantics */
532  groupList = generate_setop_grouplist(setOp, tlist);
533 
534  /* We only support hashing here */
535  if (!grouping_is_hashable(groupList))
536  ereport(ERROR,
537  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
538  errmsg("could not implement recursive UNION"),
539  errdetail("All column datatypes must be hashable.")));
540 
541  /*
542  * For the moment, take the number of distinct groups as equal to the
543  * total input size, ie, the worst case.
544  */
545  dNumGroups = lpath->rows + rpath->rows * 10;
546  }
547 
548  /*
549  * And make the path node.
550  */
551  path = (Path *) create_recursiveunion_path(root,
552  result_rel,
553  lpath,
554  rpath,
555  result_rel->reltarget,
556  groupList,
557  root->wt_param_id,
558  dNumGroups);
559 
560  add_path(result_rel, path);
561  postprocess_setop_rel(root, result_rel);
562  return result_rel;
563 }
#define list_make2(x1, x2)
Definition: pg_list.h:140
#define NIL
Definition: pg_list.h:69
static List * generate_append_tlist(List *colTypes, List *colCollations, bool flag, List *input_tlists, List *refnames_tlist)
Definition: prepunion.c:1295
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:1027
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1423
int errcode(int sqlerrcode)
Definition: elog.c:575
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:237
bool grouping_is_hashable(List *groupClause)
Definition: tlist.c:538
int wt_param_id
Definition: relation.h:324
#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:3207
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1145
struct Path * cheapest_total_path
Definition: relation.h:630
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define create_pathtarget(root, tlist)
Definition: tlist.h:69
Relids relids
Definition: relation.h:612
#define ereport(elevel, rest)
Definition: elog.h:122
List * colCollations
Definition: parsenodes.h:1608
#define Assert(condition)
Definition: c.h:699
double rows
Definition: relation.h:1088
struct Path * non_recursive_path
Definition: relation.h:325
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:284
SetOperation op
Definition: parsenodes.h:1599
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
struct PathTarget * reltarget
Definition: relation.h:623

◆ generate_setop_grouplist()

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

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

1424 {
1425  List *grouplist = copyObject(op->groupClauses);
1426  ListCell *lg;
1427  ListCell *lt;
1428 
1429  lg = list_head(grouplist);
1430  foreach(lt, targetlist)
1431  {
1432  TargetEntry *tle = (TargetEntry *) lfirst(lt);
1433  SortGroupClause *sgc;
1434 
1435  if (tle->resjunk)
1436  {
1437  /* resjunk columns should not have sortgrouprefs */
1438  Assert(tle->ressortgroupref == 0);
1439  continue; /* ignore resjunk columns */
1440  }
1441 
1442  /* non-resjunk columns should have sortgroupref = resno */
1443  Assert(tle->ressortgroupref == tle->resno);
1444 
1445  /* non-resjunk columns should have grouping clauses */
1446  Assert(lg != NULL);
1447  sgc = (SortGroupClause *) lfirst(lg);
1448  lg = lnext(lg);
1449  Assert(sgc->tleSortGroupRef == 0);
1450 
1451  sgc->tleSortGroupRef = tle->ressortgroupref;
1452  }
1453  Assert(lg == NULL);
1454  return grouplist;
1455 }
Index tleSortGroupRef
Definition: parsenodes.h:1207
bool resjunk
Definition: primnodes.h:1383
AttrNumber resno
Definition: primnodes.h:1377
static ListCell * list_head(const List *l)
Definition: pg_list.h:77
#define lnext(lc)
Definition: pg_list.h:105
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
Index ressortgroupref
Definition: primnodes.h:1379
#define copyObject(obj)
Definition: nodes.h:630
Definition: pg_list.h:45

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

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

Referenced by recurse_set_operations().

1156 {
1157  List *tlist = NIL;
1158  int resno = 1;
1159  ListCell *ctlc,
1160  *cclc,
1161  *itlc,
1162  *rtlc;
1163  TargetEntry *tle;
1164  Node *expr;
1165 
1166  /* there's no forfour() so we must chase one list manually */
1167  rtlc = list_head(refnames_tlist);
1168  forthree(ctlc, colTypes, cclc, colCollations, itlc, input_tlist)
1169  {
1170  Oid colType = lfirst_oid(ctlc);
1171  Oid colColl = lfirst_oid(cclc);
1172  TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
1173  TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
1174 
1175  rtlc = lnext(rtlc);
1176 
1177  Assert(inputtle->resno == resno);
1178  Assert(reftle->resno == resno);
1179  Assert(!inputtle->resjunk);
1180  Assert(!reftle->resjunk);
1181 
1182  /*
1183  * Generate columns referencing input columns and having appropriate
1184  * data types and column names. Insert datatype coercions where
1185  * necessary.
1186  *
1187  * HACK: constants in the input's targetlist are copied up as-is
1188  * rather than being referenced as subquery outputs. This is mainly
1189  * to ensure that when we try to coerce them to the output column's
1190  * datatype, the right things happen for UNKNOWN constants. But do
1191  * this only at the first level of subquery-scan plans; we don't want
1192  * phony constants appearing in the output tlists of upper-level
1193  * nodes!
1194  */
1195  if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
1196  expr = (Node *) inputtle->expr;
1197  else
1198  expr = (Node *) makeVar(varno,
1199  inputtle->resno,
1200  exprType((Node *) inputtle->expr),
1201  exprTypmod((Node *) inputtle->expr),
1202  exprCollation((Node *) inputtle->expr),
1203  0);
1204 
1205  if (exprType(expr) != colType)
1206  {
1207  /*
1208  * Note: it's not really cool to be applying coerce_to_common_type
1209  * here; one notable point is that assign_expr_collations never
1210  * gets run on any generated nodes. For the moment that's not a
1211  * problem because we force the correct exposed collation below.
1212  * It would likely be best to make the parser generate the correct
1213  * output tlist for every set-op to begin with, though.
1214  */
1215  expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */
1216  expr,
1217  colType,
1218  "UNION/INTERSECT/EXCEPT");
1219  }
1220 
1221  /*
1222  * Ensure the tlist entry's exposed collation matches the set-op. This
1223  * is necessary because plan_set_operations() reports the result
1224  * ordering as a list of SortGroupClauses, which don't carry collation
1225  * themselves but just refer to tlist entries. If we don't show the
1226  * right collation then planner.c might do the wrong thing in
1227  * higher-level queries.
1228  *
1229  * Note we use RelabelType, not CollateExpr, since this expression
1230  * will reach the executor without any further processing.
1231  */
1232  if (exprCollation(expr) != colColl)
1233  {
1234  expr = (Node *) makeRelabelType((Expr *) expr,
1235  exprType(expr),
1236  exprTypmod(expr),
1237  colColl,
1239  }
1240 
1241  tle = makeTargetEntry((Expr *) expr,
1242  (AttrNumber) resno++,
1243  pstrdup(reftle->resname),
1244  false);
1245 
1246  /*
1247  * By convention, all non-resjunk columns in a setop tree have
1248  * ressortgroupref equal to their resno. In some cases the ref isn't
1249  * needed, but this is a cleaner way than modifying the tlist later.
1250  */
1251  tle->ressortgroupref = tle->resno;
1252 
1253  tlist = lappend(tlist, tle);
1254  }
1255 
1256  if (flag >= 0)
1257  {
1258  /* Add a resjunk flag column */
1259  /* flag value is the given constant */
1260  expr = (Node *) makeConst(INT4OID,
1261  -1,
1262  InvalidOid,
1263  sizeof(int32),
1265  false,
1266  true);
1267  tle = makeTargetEntry((Expr *) expr,
1268  (AttrNumber) resno++,
1269  pstrdup("flag"),
1270  true);
1271  tlist = lappend(tlist, tle);
1272  }
1273 
1274  return tlist;
1275 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
int32 exprTypmod(const Node *expr)
Definition: nodeFuncs.c:276
#define forthree(cell1, list1, cell2, list2, cell3, list3)
Definition: pg_list.h:203
char * pstrdup(const char *in)
Definition: mcxt.c:1161
Definition: nodes.h:517
unsigned int Oid
Definition: postgres_ext.h:31
char * resname
Definition: primnodes.h:1378
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:298
signed int int32
Definition: c.h:313
bool resjunk
Definition: primnodes.h:1383
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:401
char * flag(int b)
Definition: test-ctype.c:33
AttrNumber resno
Definition: primnodes.h:1377
static ListCell * list_head(const List *l)
Definition: pg_list.h:77
#define lnext(lc)
Definition: pg_list.h:105
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:237
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
Node * coerce_to_common_type(ParseState *pstate, Node *node, Oid targetTypeId, const char *context)
List * lappend(List *list, void *datum)
Definition: list.c:128
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
Expr * expr
Definition: primnodes.h:1376
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:720
#define Int32GetDatum(X)
Definition: postgres.h:464
Index ressortgroupref
Definition: primnodes.h:1379
Definition: pg_list.h:45
int16 AttrNumber
Definition: attnum.h:21
#define lfirst_oid(lc)
Definition: pg_list.h:108

◆ generate_union_paths()

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

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

572 {
573  Relids relids = NULL;
574  RelOptInfo *result_rel;
575  double save_fraction = root->tuple_fraction;
576  ListCell *lc;
577  List *pathlist = NIL;
578  List *partial_pathlist = NIL;
579  bool partial_paths_valid = true;
580  bool consider_parallel = true;
581  List *rellist;
582  List *tlist_list;
583  List *tlist;
584  Path *path;
585 
586  /*
587  * If plain UNION, tell children to fetch all tuples.
588  *
589  * Note: in UNION ALL, we pass the top-level tuple_fraction unmodified to
590  * each arm of the UNION ALL. One could make a case for reducing the
591  * tuple fraction for later arms (discounting by the expected size of the
592  * earlier arms' results) but it seems not worth the trouble. The normal
593  * case where tuple_fraction isn't already zero is a LIMIT at top level,
594  * and passing it down as-is is usually enough to get the desired result
595  * of preferring fast-start plans.
596  */
597  if (!op->all)
598  root->tuple_fraction = 0.0;
599 
600  /*
601  * If any of my children are identical UNION nodes (same op, all-flag, and
602  * colTypes) then they can be merged into this node so that we generate
603  * only one Append and unique-ification for the lot. Recurse to find such
604  * nodes and compute their children's paths.
605  */
606  rellist = plan_union_children(root, op, refnames_tlist, &tlist_list);
607 
608  /*
609  * Generate tlist for Append plan node.
610  *
611  * The tlist for an Append plan isn't important as far as the Append is
612  * concerned, but we must make it look real anyway for the benefit of the
613  * next plan level up.
614  */
615  tlist = generate_append_tlist(op->colTypes, op->colCollations, false,
616  tlist_list, refnames_tlist);
617 
618  *pTargetList = tlist;
619 
620  /* Build path lists and relid set. */
621  foreach(lc, rellist)
622  {
623  RelOptInfo *rel = lfirst(lc);
624 
625  pathlist = lappend(pathlist, rel->cheapest_total_path);
626 
627  if (consider_parallel)
628  {
629  if (!rel->consider_parallel)
630  {
631  consider_parallel = false;
632  partial_paths_valid = false;
633  }
634  else if (rel->partial_pathlist == NIL)
635  partial_paths_valid = false;
636  else
637  partial_pathlist = lappend(partial_pathlist,
638  linitial(rel->partial_pathlist));
639  }
640 
641  relids = bms_union(relids, rel->relids);
642  }
643 
644  /* Build result relation. */
645  result_rel = fetch_upper_rel(root, UPPERREL_SETOP, relids);
646  result_rel->reltarget = create_pathtarget(root, tlist);
647  result_rel->consider_parallel = consider_parallel;
648 
649  /*
650  * Append the child results together.
651  */
652  path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
653  NULL, 0, false, NIL, -1);
654 
655  /*
656  * For UNION ALL, we just need the Append path. For UNION, need to add
657  * node(s) to remove duplicates.
658  */
659  if (!op->all)
660  path = make_union_unique(op, path, tlist, root);
661 
662  add_path(result_rel, path);
663 
664  /*
665  * Estimate number of groups. For now we just assume the output is unique
666  * --- this is certainly true for the UNION case, and we want worst-case
667  * estimates anyway.
668  */
669  result_rel->rows = path->rows;
670 
671  /*
672  * Now consider doing the same thing using the partial paths plus Append
673  * plus Gather.
674  */
675  if (partial_paths_valid)
676  {
677  Path *ppath;
678  ListCell *lc;
679  int parallel_workers = 0;
680 
681  /* Find the highest number of workers requested for any subpath. */
682  foreach(lc, partial_pathlist)
683  {
684  Path *path = lfirst(lc);
685 
686  parallel_workers = Max(parallel_workers, path->parallel_workers);
687  }
688  Assert(parallel_workers > 0);
689 
690  /*
691  * If the use of parallel append is permitted, always request at least
692  * log2(# of children) paths. We assume it can be useful to have
693  * extra workers in this case because they will be spread out across
694  * the children. The precise formula is just a guess; see
695  * add_paths_to_append_rel.
696  */
698  {
699  parallel_workers = Max(parallel_workers,
700  fls(list_length(partial_pathlist)));
701  parallel_workers = Min(parallel_workers,
703  }
704  Assert(parallel_workers > 0);
705 
706  ppath = (Path *)
707  create_append_path(root, result_rel, NIL, partial_pathlist,
708  NULL, parallel_workers, enable_parallel_append,
709  NIL, -1);
710  ppath = (Path *)
711  create_gather_path(root, result_rel, ppath,
712  result_rel->reltarget, NULL, NULL);
713  if (!op->all)
714  ppath = make_union_unique(op, ppath, tlist, root);
715  add_path(result_rel, ppath);
716  }
717 
718  /* Undo effects of possibly forcing tuple_fraction to 0 */
719  root->tuple_fraction = save_fraction;
720 
721  return result_rel;
722 }
#define NIL
Definition: pg_list.h:69
static List * generate_append_tlist(List *colTypes, List *colCollations, bool flag, List *input_tlists, List *refnames_tlist)
Definition: prepunion.c:1295
AppendPath * create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *partial_subpaths, Relids required_outer, int parallel_workers, bool parallel_aware, List *partitioned_rels, double rows)
Definition: pathnode.c:1219
GatherPath * create_gather_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target, Relids required_outer, double *rows)
Definition: pathnode.c:1832
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
#define Min(x, y)
Definition: c.h:857
int parallel_workers
Definition: relation.h:1085
static Path * make_union_unique(SetOperationStmt *op, Path *path, List *tlist, PlannerInfo *root)
Definition: prepunion.c:964
List * partial_pathlist
Definition: relation.h:628
bool enable_parallel_append
Definition: costsize.c:139
int fls(int mask)
Definition: fls.c:55
double tuple_fraction
Definition: relation.h:306
#define linitial(l)
Definition: pg_list.h:111
static List * plan_union_children(PlannerInfo *root, SetOperationStmt *top_union, List *refnames_tlist, List **tlist_list)
Definition: prepunion.c:905
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1145
struct Path * cheapest_total_path
Definition: relation.h:630
#define create_pathtarget(root, tlist)
Definition: tlist.h:69
Relids relids
Definition: relation.h:612
List * lappend(List *list, void *datum)
Definition: list.c:128
List * colCollations
Definition: parsenodes.h:1608
double rows
Definition: relation.h:615
#define Max(x, y)
Definition: c.h:851
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
double rows
Definition: relation.h:1088
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:284
static int list_length(const List *l)
Definition: pg_list.h:89
bool consider_parallel
Definition: relation.h:620
int max_parallel_workers_per_gather
Definition: costsize.c:123
Definition: pg_list.h:45
struct PathTarget * reltarget
Definition: relation.h:623

◆ make_inh_translation_list()

static void make_inh_translation_list ( Relation  oldrelation,
Relation  newrelation,
Index  newvarno,
List **  translated_vars 
)
static

Definition at line 1886 of file prepunion.c.

References attcollation, attname, atttypid, elog, ERROR, lappend(), makeVar(), NameStr, tupleDesc::natts, NIL, RelationGetDescr, RelationGetRelationName, and TupleDescAttr.

Referenced by expand_single_inheritance_child().

1889 {
1890  List *vars = NIL;
1891  TupleDesc old_tupdesc = RelationGetDescr(oldrelation);
1892  TupleDesc new_tupdesc = RelationGetDescr(newrelation);
1893  int oldnatts = old_tupdesc->natts;
1894  int newnatts = new_tupdesc->natts;
1895  int old_attno;
1896 
1897  for (old_attno = 0; old_attno < oldnatts; old_attno++)
1898  {
1899  Form_pg_attribute att;
1900  char *attname;
1901  Oid atttypid;
1902  int32 atttypmod;
1903  Oid attcollation;
1904  int new_attno;
1905 
1906  att = TupleDescAttr(old_tupdesc, old_attno);
1907  if (att->attisdropped)
1908  {
1909  /* Just put NULL into this list entry */
1910  vars = lappend(vars, NULL);
1911  continue;
1912  }
1913  attname = NameStr(att->attname);
1914  atttypid = att->atttypid;
1915  atttypmod = att->atttypmod;
1916  attcollation = att->attcollation;
1917 
1918  /*
1919  * When we are generating the "translation list" for the parent table
1920  * of an inheritance set, no need to search for matches.
1921  */
1922  if (oldrelation == newrelation)
1923  {
1924  vars = lappend(vars, makeVar(newvarno,
1925  (AttrNumber) (old_attno + 1),
1926  atttypid,
1927  atttypmod,
1928  attcollation,
1929  0));
1930  continue;
1931  }
1932 
1933  /*
1934  * Otherwise we have to search for the matching column by name.
1935  * There's no guarantee it'll have the same column position, because
1936  * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.
1937  * However, in simple cases it will be the same column number, so try
1938  * that before we go groveling through all the columns.
1939  *
1940  * Note: the test for (att = ...) != NULL cannot fail, it's just a
1941  * notational device to include the assignment into the if-clause.
1942  */
1943  if (old_attno < newnatts &&
1944  (att = TupleDescAttr(new_tupdesc, old_attno)) != NULL &&
1945  !att->attisdropped &&
1946  strcmp(attname, NameStr(att->attname)) == 0)
1947  new_attno = old_attno;
1948  else
1949  {
1950  for (new_attno = 0; new_attno < newnatts; new_attno++)
1951  {
1952  att = TupleDescAttr(new_tupdesc, new_attno);
1953  if (!att->attisdropped &&
1954  strcmp(attname, NameStr(att->attname)) == 0)
1955  break;
1956  }
1957  if (new_attno >= newnatts)
1958  elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
1959  attname, RelationGetRelationName(newrelation));
1960  }
1961 
1962  /* Found it, check type and collation match */
1963  if (atttypid != att->atttypid || atttypmod != att->atttypmod)
1964  elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
1965  attname, RelationGetRelationName(newrelation));
1966  if (attcollation != att->attcollation)
1967  elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
1968  attname, RelationGetRelationName(newrelation));
1969 
1970  vars = lappend(vars, makeVar(newvarno,
1971  (AttrNumber) (new_attno + 1),
1972  atttypid,
1973  atttypmod,
1974  attcollation,
1975  0));
1976  }
1977 
1978  *translated_vars = vars;
1979 }
#define NIL
Definition: pg_list.h:69
#define RelationGetDescr(relation)
Definition: rel.h:433
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:93
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:82
signed int int32
Definition: c.h:313
#define ERROR
Definition: elog.h:43
NameData attname
Definition: pg_attribute.h:40
Oid attcollation
Definition: pg_attribute.h:161
#define RelationGetRelationName(relation)
Definition: rel.h:441
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:197
Oid atttypid
Definition: pg_attribute.h:49
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
List * lappend(List *list, void *datum)
Definition: list.c:128
#define NameStr(name)
Definition: c.h:576
#define elog
Definition: elog.h:219
Definition: regcomp.c:224
Definition: pg_list.h:45
int16 AttrNumber
Definition: attnum.h:21

◆ make_union_unique()

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

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

966 {
967  RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
968  List *groupList;
969  double dNumGroups;
970 
971  /* Identify the grouping semantics */
972  groupList = generate_setop_grouplist(op, tlist);
973 
974  /*
975  * XXX for the moment, take the number of distinct groups as equal to the
976  * total input size, ie, the worst case. This is too conservative, but we
977  * don't want to risk having the hashtable overrun memory; also, it's not
978  * clear how to get a decent estimate of the true size. One should note
979  * as well the propensity of novices to write UNION rather than UNION ALL
980  * even when they don't expect any duplicates...
981  */
982  dNumGroups = path->rows;
983 
984  /* Decide whether to hash or sort */
985  if (choose_hashed_setop(root, groupList, path,
986  dNumGroups, dNumGroups,
987  "UNION"))
988  {
989  /* Hashed aggregate plan --- no sort needed */
990  path = (Path *) create_agg_path(root,
991  result_rel,
992  path,
993  create_pathtarget(root, tlist),
994  AGG_HASHED,
996  groupList,
997  NIL,
998  NULL,
999  dNumGroups);
1000  }
1001  else
1002  {
1003  /* Sort and Unique */
1004  if (groupList)
1005  path = (Path *)
1006  create_sort_path(root,
1007  result_rel,
1008  path,
1010  groupList,
1011  tlist),
1012  -1.0);
1013  path = (Path *) create_upper_unique_path(root,
1014  result_rel,
1015  path,
1016  list_length(path->pathkeys),
1017  dNumGroups);
1018  }
1019 
1020  return path;
1021 }
#define NIL
Definition: pg_list.h:69
List * make_pathkeys_for_sortclauses(PlannerInfo *root, List *sortclauses, List *tlist)
Definition: pathkeys.c:874
UpperUniquePath * create_upper_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, int numCols, double numGroups)
Definition: pathnode.c:2734
static List * generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Definition: prepunion.c:1423
static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses, Path *input_path, double dNumGroups, double dNumOutputRows, const char *construct)
Definition: prepunion.c:1045
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1145
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:2786
#define create_pathtarget(root, tlist)
Definition: tlist.h:69
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2631
List * pathkeys
Definition: relation.h:1092
double rows
Definition: relation.h:1088
static int list_length(const List *l)
Definition: pg_list.h:89
Definition: pg_list.h:45

◆ plan_set_operations()

RelOptInfo* plan_set_operations ( PlannerInfo root)

Definition at line 142 of file prepunion.c.

References Assert, castNode, Query::distinctClause, 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().

143 {
144  Query *parse = root->parse;
146  Node *node;
147  RangeTblEntry *leftmostRTE;
148  Query *leftmostQuery;
149  RelOptInfo *setop_rel;
150  List *top_tlist;
151 
152  Assert(topop);
153 
154  /* check for unsupported stuff */
155  Assert(parse->jointree->fromlist == NIL);
156  Assert(parse->jointree->quals == NULL);
157  Assert(parse->groupClause == NIL);
158  Assert(parse->havingQual == NULL);
159  Assert(parse->windowClause == NIL);
160  Assert(parse->distinctClause == NIL);
161 
162  /*
163  * We'll need to build RelOptInfos for each of the leaf subqueries, which
164  * are RTE_SUBQUERY rangetable entries in this Query. Prepare the index
165  * arrays for that.
166  */
168 
169  /*
170  * Find the leftmost component Query. We need to use its column names for
171  * all generated tlists (else SELECT INTO won't work right).
172  */
173  node = topop->larg;
174  while (node && IsA(node, SetOperationStmt))
175  node = ((SetOperationStmt *) node)->larg;
176  Assert(node && IsA(node, RangeTblRef));
177  leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex];
178  leftmostQuery = leftmostRTE->subquery;
179  Assert(leftmostQuery != NULL);
180 
181  /*
182  * If the topmost node is a recursive union, it needs special processing.
183  */
184  if (root->hasRecursion)
185  {
186  setop_rel = generate_recursion_path(topop, root,
187  leftmostQuery->targetList,
188  &top_tlist);
189  }
190  else
191  {
192  /*
193  * Recurse on setOperations tree to generate paths for set ops. The
194  * final output paths should have just the column types shown as the
195  * output from the top-level node, plus possibly resjunk working
196  * columns (we can rely on upper-level nodes to deal with that).
197  */
198  setop_rel = recurse_set_operations((Node *) topop, root,
199  topop->colTypes, topop->colCollations,
200  true, -1,
201  leftmostQuery->targetList,
202  &top_tlist,
203  NULL);
204  }
205 
206  /* Must return the built tlist into root->processed_tlist. */
207  root->processed_tlist = top_tlist;
208 
209  return setop_rel;
210 }
static RelOptInfo * generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition: prepunion.c:463
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
Query * parse
Definition: relation.h:169
FromExpr * jointree
Definition: parsenodes.h:138
#define castNode(_type_, nodeptr)
Definition: nodes.h:586
Definition: nodes.h:517
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:237
List * fromlist
Definition: primnodes.h:1479
bool hasRecursion
Definition: relation.h:321
Node * quals
Definition: primnodes.h:1480
List * windowClause
Definition: parsenodes.h:154
List * targetList
Definition: parsenodes.h:140
List * distinctClause
Definition: parsenodes.h:156
RangeTblEntry ** simple_rte_array
Definition: relation.h:202
#define Assert(condition)
Definition: c.h:699
void setup_simple_rel_arrays(PlannerInfo *root)
Definition: relnode.c:67
Node * setOperations
Definition: parsenodes.h:165
Query * subquery
Definition: parsenodes.h:985
List * groupClause
Definition: parsenodes.h:148
Node * havingQual
Definition: parsenodes.h:152
List * processed_tlist
Definition: relation.h:296
Definition: pg_list.h:45
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:649

◆ plan_union_children()

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

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

909 {
910  List *pending_rels = list_make1(top_union);
911  List *result = NIL;
912  List *child_tlist;
913 
914  *tlist_list = NIL;
915 
916  while (pending_rels != NIL)
917  {
918  Node *setOp = linitial(pending_rels);
919 
920  pending_rels = list_delete_first(pending_rels);
921 
922  if (IsA(setOp, SetOperationStmt))
923  {
924  SetOperationStmt *op = (SetOperationStmt *) setOp;
925 
926  if (op->op == top_union->op &&
927  (op->all == top_union->all || op->all) &&
928  equal(op->colTypes, top_union->colTypes))
929  {
930  /* Same UNION, so fold children into parent */
931  pending_rels = lcons(op->rarg, pending_rels);
932  pending_rels = lcons(op->larg, pending_rels);
933  continue;
934  }
935  }
936 
937  /*
938  * Not same, so plan this child separately.
939  *
940  * Note we disallow any resjunk columns in child results. This is
941  * necessary since the Append node that implements the union won't do
942  * any projection, and upper levels will get confused if some of our
943  * output tuples have junk and some don't. This case only arises when
944  * we have an EXCEPT or INTERSECT as child, else there won't be
945  * resjunk anyway.
946  */
947  result = lappend(result, recurse_set_operations(setOp, root,
948  top_union->colTypes,
949  top_union->colCollations,
950  false, -1,
951  refnames_tlist,
952  &child_tlist,
953  NULL));
954  *tlist_list = lappend(*tlist_list, child_tlist);
955  }
956 
957  return result;
958 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2986
Definition: nodes.h:517
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:237
#define list_make1(x1)
Definition: pg_list.h:139
#define linitial(l)
Definition: pg_list.h:111
List * lappend(List *list, void *datum)
Definition: list.c:128
List * colCollations
Definition: parsenodes.h:1608
List * lcons(void *datum, List *list)
Definition: list.c:259
SetOperation op
Definition: parsenodes.h:1599
Definition: pg_list.h:45
List * list_delete_first(List *list)
Definition: list.c:666

◆ postprocess_setop_rel()

static void postprocess_setop_rel ( PlannerInfo root,
RelOptInfo rel 
)
static

Definition at line 1027 of file prepunion.c.

References create_upper_paths_hook, set_cheapest(), and UPPERREL_SETOP.

Referenced by generate_recursion_path(), and recurse_set_operations().

1028 {
1029  /*
1030  * We don't currently worry about allowing FDWs to contribute paths to
1031  * this relation, but give extensions a chance.
1032  */
1034  (*create_upper_paths_hook) (root, UPPERREL_SETOP,
1035  NULL, rel, NULL);
1036 
1037  /* Select cheapest path */
1038  set_cheapest(rel);
1039 }
create_upper_paths_hook_type create_upper_paths_hook
Definition: planner.c:71
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 237 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().

243 {
244  RelOptInfo *rel = NULL; /* keep compiler quiet */
245 
246  /* Guard against stack overflow due to overly complex setop nests */
248 
249  if (IsA(setOp, RangeTblRef))
250  {
251  RangeTblRef *rtr = (RangeTblRef *) setOp;
252  RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
253  Query *subquery = rte->subquery;
254  PlannerInfo *subroot;
255  RelOptInfo *final_rel;
256  Path *subpath;
257  Path *path;
258  List *tlist;
259 
260  Assert(subquery != NULL);
261 
262  /* Build a RelOptInfo for this leaf subquery. */
263  rel = build_simple_rel(root, rtr->rtindex, NULL);
264 
265  /* plan_params should not be in use in current query level */
266  Assert(root->plan_params == NIL);
267 
268  /* Generate a subroot and Paths for the subquery */
269  subroot = rel->subroot = subquery_planner(root->glob, subquery,
270  root,
271  false,
272  root->tuple_fraction);
273 
274  /*
275  * It should not be possible for the primitive query to contain any
276  * cross-references to other primitive queries in the setop tree.
277  */
278  if (root->plan_params)
279  elog(ERROR, "unexpected outer reference in set operation subquery");
280 
281  /* Figure out the appropriate target list for this subquery. */
282  tlist = generate_setop_tlist(colTypes, colCollations,
283  flag,
284  rtr->rtindex,
285  true,
286  subroot->processed_tlist,
287  refnames_tlist);
288  rel->reltarget = create_pathtarget(root, tlist);
289 
290  /* Return the fully-fledged tlist to caller, too */
291  *pTargetList = tlist;
292 
293  /*
294  * Mark rel with estimated output rows, width, etc. Note that we have
295  * to do this before generating outer-query paths, else
296  * cost_subqueryscan is not happy.
297  */
298  set_subquery_size_estimates(root, rel);
299 
300  /*
301  * Since we may want to add a partial path to this relation, we must
302  * set its consider_parallel flag correctly.
303  */
304  final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
305  rel->consider_parallel = final_rel->consider_parallel;
306 
307  /*
308  * For the moment, we consider only a single Path for the subquery.
309  * This should change soon (make it look more like
310  * set_subquery_pathlist).
311  */
312  subpath = get_cheapest_fractional_path(final_rel,
313  root->tuple_fraction);
314 
315  /*
316  * Stick a SubqueryScanPath atop that.
317  *
318  * We don't bother to determine the subquery's output ordering since
319  * it won't be reflected in the set-op result anyhow; so just label
320  * the SubqueryScanPath with nil pathkeys. (XXX that should change
321  * soon too, likely.)
322  */
323  path = (Path *) create_subqueryscan_path(root, rel, subpath,
324  NIL, NULL);
325 
326  add_path(rel, path);
327 
328  /*
329  * If we have a partial path for the child relation, we can use that
330  * to build a partial path for this relation. But there's no point in
331  * considering any path but the cheapest.
332  */
333  if (rel->consider_parallel && bms_is_empty(rel->lateral_relids) &&
334  final_rel->partial_pathlist != NIL)
335  {
336  Path *partial_subpath;
337  Path *partial_path;
338 
339  partial_subpath = linitial(final_rel->partial_pathlist);
340  partial_path = (Path *)
341  create_subqueryscan_path(root, rel, partial_subpath,
342  NIL, NULL);
343  add_partial_path(rel, partial_path);
344  }
345 
346  /*
347  * Estimate number of groups if caller wants it. If the subquery used
348  * grouping or aggregation, its output is probably mostly unique
349  * anyway; otherwise do statistical estimation.
350  *
351  * XXX you don't really want to know about this: we do the estimation
352  * using the subquery's original targetlist expressions, not the
353  * subroot->processed_tlist which might seem more appropriate. The
354  * reason is that if the subquery is itself a setop, it may return a
355  * processed_tlist containing "varno 0" Vars generated by
356  * generate_append_tlist, and those would confuse estimate_num_groups
357  * mightily. We ought to get rid of the "varno 0" hack, but that
358  * requires a redesign of the parsetree representation of setops, so
359  * that there can be an RTE corresponding to each setop's output.
360  */
361  if (pNumGroups)
362  {
363  if (subquery->groupClause || subquery->groupingSets ||
364  subquery->distinctClause ||
365  subroot->hasHavingQual || subquery->hasAggs)
366  *pNumGroups = subpath->rows;
367  else
368  *pNumGroups = estimate_num_groups(subroot,
369  get_tlist_exprs(subquery->targetList, false),
370  subpath->rows,
371  NULL);
372  }
373  }
374  else if (IsA(setOp, SetOperationStmt))
375  {
376  SetOperationStmt *op = (SetOperationStmt *) setOp;
377 
378  /* UNIONs are much different from INTERSECT/EXCEPT */
379  if (op->op == SETOP_UNION)
380  rel = generate_union_paths(op, root,
381  refnames_tlist,
382  pTargetList);
383  else
384  rel = generate_nonunion_paths(op, root,
385  refnames_tlist,
386  pTargetList);
387  if (pNumGroups)
388  *pNumGroups = rel->rows;
389 
390  /*
391  * If necessary, add a Result node to project the caller-requested
392  * output columns.
393  *
394  * XXX you don't really want to know about this: setrefs.c will apply
395  * fix_upper_expr() to the Result node's tlist. This would fail if the
396  * Vars generated by generate_setop_tlist() were not exactly equal()
397  * to the corresponding tlist entries of the subplan. However, since
398  * the subplan was generated by generate_union_plan() or
399  * generate_nonunion_plan(), and hence its tlist was generated by
400  * generate_append_tlist(), this will work. We just tell
401  * generate_setop_tlist() to use varno 0.
402  */
403  if (flag >= 0 ||
404  !tlist_same_datatypes(*pTargetList, colTypes, junkOK) ||
405  !tlist_same_collations(*pTargetList, colCollations, junkOK))
406  {
407  PathTarget *target;
408  ListCell *lc;
409 
410  *pTargetList = generate_setop_tlist(colTypes, colCollations,
411  flag,
412  0,
413  false,
414  *pTargetList,
415  refnames_tlist);
416  target = create_pathtarget(root, *pTargetList);
417 
418  /* Apply projection to each path */
419  foreach(lc, rel->pathlist)
420  {
421  Path *subpath = (Path *) lfirst(lc);
422  Path *path;
423 
424  Assert(subpath->param_info == NULL);
425  path = apply_projection_to_path(root, subpath->parent,
426  subpath, target);
427  /* If we had to add a Result, path is different from subpath */
428  if (path != subpath)
429  lfirst(lc) = path;
430  }
431 
432  /* Apply projection to each partial path */
433  foreach(lc, rel->partial_pathlist)
434  {
435  Path *subpath = (Path *) lfirst(lc);
436  Path *path;
437 
438  Assert(subpath->param_info == NULL);
439 
440  /* avoid apply_projection_to_path, in case of multiple refs */
441  path = (Path *) create_projection_path(root, subpath->parent,
442  subpath, target);
443  lfirst(lc) = path;
444  }
445  }
446  }
447  else
448  {
449  elog(ERROR, "unrecognized node type: %d",
450  (int) nodeTag(setOp));
451  *pTargetList = NIL;
452  }
453 
454  postprocess_setop_rel(root, rel);
455 
456  return rel;
457 }
Path * apply_projection_to_path(PlannerInfo *root, RelOptInfo *rel, Path *path, PathTarget *target)
Definition: pathnode.c:2475
void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition: costsize.c:4810
#define NIL
Definition: pg_list.h:69
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset)
Definition: selfuncs.c:3419
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
Definition: tlist.c:251
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:1871
List * plan_params
Definition: relation.h:183
static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
Definition: prepunion.c:1027
static RelOptInfo * generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition: prepunion.c:728
bool hasAggs
Definition: parsenodes.h:125
ParamPathInfo * param_info
Definition: relation.h:1081
List * groupingSets
Definition: parsenodes.h:150
ProjectionPath * create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target)
Definition: pathnode.c:2384
List * partial_pathlist
Definition: relation.h:628
List * targetList
Definition: parsenodes.h:140
PlannerInfo * subroot
Definition: relation.h:654
Relids lateral_relids
Definition: relation.h:637
double tuple_fraction
Definition: relation.h:306
#define linitial(l)
Definition: pg_list.h:111
List * distinctClause
Definition: parsenodes.h:156
#define ERROR
Definition: elog.h:43
RelOptInfo * parent
Definition: relation.h:1078
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1145
static RelOptInfo * generate_union_paths(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition: prepunion.c:569
void check_stack_depth(void)
Definition: postgres.c:3159
PlannerGlobal * glob
Definition: relation.h:171
char * flag(int b)
Definition: test-ctype.c:33
#define create_pathtarget(root, tlist)
Definition: tlist.h:69
RelOptInfo * build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
Definition: relnode.c:96
RangeTblEntry ** simple_rte_array
Definition: relation.h:202
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:729
List * get_tlist_exprs(List *tlist, bool includeJunk)
Definition: tlist.c:166
double rows
Definition: relation.h:615
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
double rows
Definition: relation.h:1088
SetOperation op
Definition: parsenodes.h:1599
bool consider_parallel
Definition: relation.h:620
#define nodeTag(nodeptr)
Definition: nodes.h:522
Query * subquery
Definition: parsenodes.h:985
List * groupClause
Definition: parsenodes.h:148
void add_partial_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:762
bool hasHavingQual
Definition: relation.h:318
List * pathlist
Definition: relation.h:626
bool tlist_same_collations(List *tlist, List *colCollations, bool junkOK)
Definition: tlist.c:285
#define elog
Definition: elog.h:219
List * processed_tlist
Definition: relation.h:296
Definition: pg_list.h:45
struct PathTarget * reltarget
Definition: relation.h:623
PlannerInfo * subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, bool hasRecursion, double tuple_fraction)
Definition: planner.c:594
Datum subpath(PG_FUNCTION_ARGS)
Definition: ltree_op.c:234
Path * get_cheapest_fractional_path(RelOptInfo *rel, double tuple_fraction)
Definition: planner.c:5735
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:1150

◆ translate_col_privs()

static Bitmapset * translate_col_privs ( const Bitmapset parent_privs,
List translated_vars 
)
static

Definition at line 1993 of file prepunion.c.

References bms_add_member(), bms_is_member(), FirstLowInvalidHeapAttributeNumber, InvalidAttrNumber, lfirst_node, and Var::varattno.

Referenced by expand_single_inheritance_child().

1995 {
1996  Bitmapset *child_privs = NULL;
1997  bool whole_row;
1998  int attno;
1999  ListCell *lc;
2000 
2001  /* System attributes have the same numbers in all tables */
2002  for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)
2003  {
2005  parent_privs))
2006  child_privs = bms_add_member(child_privs,
2008  }
2009 
2010  /* Check if parent has whole-row reference */
2012  parent_privs);
2013 
2014  /* And now translate the regular user attributes, using the vars list */
2015  attno = InvalidAttrNumber;
2016  foreach(lc, translated_vars)
2017  {
2018  Var *var = lfirst_node(Var, lc);
2019 
2020  attno++;
2021  if (var == NULL) /* ignore dropped columns */
2022  continue;
2023  if (whole_row ||
2025  parent_privs))
2026  child_privs = bms_add_member(child_privs,
2028  }
2029 
2030  return child_privs;
2031 }
AttrNumber varattno
Definition: primnodes.h:169
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:28
Definition: primnodes.h:164
#define lfirst_node(type, lc)
Definition: pg_list.h:109
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:764
#define InvalidAttrNumber
Definition: attnum.h:23
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:486