PostgreSQL Source Code  git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
prepjointree.c File Reference
#include "postgres.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/multibitmapset.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "optimizer/placeholder.h"
#include "optimizer/prep.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
Include dependency graph for prepjointree.c:

Go to the source code of this file.

Data Structures

struct  nullingrel_info
 
struct  pullup_replace_vars_context
 
struct  reduce_outer_joins_pass1_state
 
struct  reduce_outer_joins_pass2_state
 
struct  reduce_outer_joins_partial_state
 
struct  find_dependent_phvs_context
 
struct  substitute_phv_relids_context
 

Typedefs

typedef struct nullingrel_info nullingrel_info
 
typedef struct pullup_replace_vars_context pullup_replace_vars_context
 
typedef struct reduce_outer_joins_pass1_state reduce_outer_joins_pass1_state
 
typedef struct reduce_outer_joins_pass2_state reduce_outer_joins_pass2_state
 
typedef struct reduce_outer_joins_partial_state reduce_outer_joins_partial_state
 

Functions

static Nodepull_up_sublinks_jointree_recurse (PlannerInfo *root, Node *jtnode, Relids *relids)
 
static Nodepull_up_sublinks_qual_recurse (PlannerInfo *root, Node *node, Node **jtlink1, Relids available_rels1, Node **jtlink2, Relids available_rels2)
 
static Nodepull_up_subqueries_recurse (PlannerInfo *root, Node *jtnode, JoinExpr *lowest_outer_join, AppendRelInfo *containing_appendrel)
 
static Nodepull_up_simple_subquery (PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, JoinExpr *lowest_outer_join, AppendRelInfo *containing_appendrel)
 
static Nodepull_up_simple_union_all (PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
 
static void pull_up_union_leaf_queries (Node *setOp, PlannerInfo *root, int parentRTindex, Query *setOpQuery, int childRToffset)
 
static void make_setop_translation_list (Query *query, int newvarno, AppendRelInfo *appinfo)
 
static bool is_simple_subquery (PlannerInfo *root, Query *subquery, RangeTblEntry *rte, JoinExpr *lowest_outer_join)
 
static Nodepull_up_simple_values (PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
 
static bool is_simple_values (PlannerInfo *root, RangeTblEntry *rte)
 
static Nodepull_up_constant_function (PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, AppendRelInfo *containing_appendrel)
 
static bool is_simple_union_all (Query *subquery)
 
static bool is_simple_union_all_recurse (Node *setOp, Query *setOpQuery, List *colTypes)
 
static bool is_safe_append_member (Query *subquery)
 
static bool jointree_contains_lateral_outer_refs (PlannerInfo *root, Node *jtnode, bool restricted, Relids safe_upper_varnos)
 
static void perform_pullup_replace_vars (PlannerInfo *root, pullup_replace_vars_context *rvcontext, AppendRelInfo *containing_appendrel)
 
static void replace_vars_in_jointree (Node *jtnode, pullup_replace_vars_context *context)
 
static Nodepullup_replace_vars (Node *expr, pullup_replace_vars_context *context)
 
static Nodepullup_replace_vars_callback (Var *var, replace_rte_variables_context *context)
 
static Querypullup_replace_vars_subquery (Query *query, pullup_replace_vars_context *context)
 
static reduce_outer_joins_pass1_statereduce_outer_joins_pass1 (Node *jtnode)
 
static void reduce_outer_joins_pass2 (Node *jtnode, reduce_outer_joins_pass1_state *state1, reduce_outer_joins_pass2_state *state2, PlannerInfo *root, Relids nonnullable_rels, List *forced_null_vars)
 
static void report_reduced_full_join (reduce_outer_joins_pass2_state *state2, int rtindex, Relids relids)
 
static Noderemove_useless_results_recurse (PlannerInfo *root, Node *jtnode, Node **parent_quals, Relids *dropped_outer_joins)
 
static int get_result_relid (PlannerInfo *root, Node *jtnode)
 
static void remove_result_refs (PlannerInfo *root, int varno, Node *newjtloc)
 
static bool find_dependent_phvs (PlannerInfo *root, int varno)
 
static bool find_dependent_phvs_in_jointree (PlannerInfo *root, Node *node, int varno)
 
static void substitute_phv_relids (Node *node, int varno, Relids subrelids)
 
static void fix_append_rel_relids (PlannerInfo *root, int varno, Relids subrelids)
 
static Nodefind_jointree_node_for_rel (Node *jtnode, int relid)
 
static nullingrel_infoget_nullingrels (Query *parse)
 
static void get_nullingrels_recurse (Node *jtnode, Relids upper_nullingrels, nullingrel_info *info)
 
void transform_MERGE_to_join (Query *parse)
 
void replace_empty_jointree (Query *parse)
 
void pull_up_sublinks (PlannerInfo *root)
 
void preprocess_function_rtes (PlannerInfo *root)
 
void pull_up_subqueries (PlannerInfo *root)
 
void flatten_simple_union_all (PlannerInfo *root)
 
void reduce_outer_joins (PlannerInfo *root)
 
void remove_useless_result_rtes (PlannerInfo *root)
 
static bool find_dependent_phvs_walker (Node *node, find_dependent_phvs_context *context)
 
static bool substitute_phv_relids_walker (Node *node, substitute_phv_relids_context *context)
 
Relids get_relids_in_jointree (Node *jtnode, bool include_outer_joins, bool include_inner_joins)
 
Relids get_relids_for_join (Query *query, int joinrelid)
 

Typedef Documentation

◆ nullingrel_info

◆ pullup_replace_vars_context

◆ reduce_outer_joins_partial_state

◆ reduce_outer_joins_pass1_state

◆ reduce_outer_joins_pass2_state

Function Documentation

◆ find_dependent_phvs()

static bool find_dependent_phvs ( PlannerInfo root,
int  varno 
)
static

Definition at line 3876 of file prepjointree.c.

3877 {
3879 
3880  /* If there are no PHVs anywhere, we needn't work hard */
3881  if (root->glob->lastPHId == 0)
3882  return false;
3883 
3884  context.relids = bms_make_singleton(varno);
3885  context.sublevels_up = 0;
3886 
3888  return true;
3889  /* The append_rel_list could be populated already, so check it too */
3890  if (expression_tree_walker((Node *) root->append_rel_list,
3892  &context))
3893  return true;
3894  return false;
3895 }
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
#define query_tree_walker(q, w, c, f)
Definition: nodeFuncs.h:158
#define expression_tree_walker(n, w, c)
Definition: nodeFuncs.h:153
static bool find_dependent_phvs_walker(Node *node, find_dependent_phvs_context *context)
tree context
Definition: radixtree.h:1835
tree ctl root
Definition: radixtree.h:1886
Definition: nodes.h:129

References bms_make_singleton(), context, expression_tree_walker, find_dependent_phvs_walker(), query_tree_walker, and root.

Referenced by remove_useless_results_recurse().

◆ find_dependent_phvs_in_jointree()

static bool find_dependent_phvs_in_jointree ( PlannerInfo root,
Node node,
int  varno 
)
static

Definition at line 3898 of file prepjointree.c.

3899 {
3901  Relids subrelids;
3902  int relid;
3903 
3904  /* If there are no PHVs anywhere, we needn't work hard */
3905  if (root->glob->lastPHId == 0)
3906  return false;
3907 
3908  context.relids = bms_make_singleton(varno);
3909  context.sublevels_up = 0;
3910 
3911  /*
3912  * See if the jointree fragment itself contains references (in join quals)
3913  */
3914  if (find_dependent_phvs_walker(node, &context))
3915  return true;
3916 
3917  /*
3918  * Otherwise, identify the set of referenced RTEs (we can ignore joins,
3919  * since they should be flattened already, so their join alias lists no
3920  * longer matter), and tediously check each RTE. We can ignore RTEs that
3921  * are not marked LATERAL, though, since they couldn't possibly contain
3922  * any cross-references to other RTEs.
3923  */
3924  subrelids = get_relids_in_jointree(node, false, false);
3925  relid = -1;
3926  while ((relid = bms_next_member(subrelids, relid)) >= 0)
3927  {
3928  RangeTblEntry *rte = rt_fetch(relid, root->parse->rtable);
3929 
3930  if (rte->lateral &&
3932  return true;
3933  }
3934 
3935  return false;
3936 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
#define range_table_entry_walker(r, w, c, f)
Definition: nodeFuncs.h:168
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
Relids get_relids_in_jointree(Node *jtnode, bool include_outer_joins, bool include_inner_joins)

References bms_make_singleton(), bms_next_member(), context, find_dependent_phvs_walker(), get_relids_in_jointree(), range_table_entry_walker, root, and rt_fetch.

Referenced by remove_useless_results_recurse().

◆ find_dependent_phvs_walker()

static bool find_dependent_phvs_walker ( Node node,
find_dependent_phvs_context context 
)
static

Definition at line 3841 of file prepjointree.c.

3843 {
3844  if (node == NULL)
3845  return false;
3846  if (IsA(node, PlaceHolderVar))
3847  {
3848  PlaceHolderVar *phv = (PlaceHolderVar *) node;
3849 
3850  if (phv->phlevelsup == context->sublevels_up &&
3851  bms_equal(context->relids, phv->phrels))
3852  return true;
3853  /* fall through to examine children */
3854  }
3855  if (IsA(node, Query))
3856  {
3857  /* Recurse into subselects */
3858  bool result;
3859 
3860  context->sublevels_up++;
3861  result = query_tree_walker((Query *) node,
3863  context, 0);
3864  context->sublevels_up--;
3865  return result;
3866  }
3867  /* Shouldn't need to handle most planner auxiliary nodes here */
3868  Assert(!IsA(node, SpecialJoinInfo));
3869  Assert(!IsA(node, PlaceHolderInfo));
3870  Assert(!IsA(node, MinMaxAggInfo));
3871 
3873 }
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:142
#define Assert(condition)
Definition: c.h:837
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
Index phlevelsup
Definition: pathnodes.h:2807

References Assert, bms_equal(), context, expression_tree_walker, IsA, PlaceHolderVar::phlevelsup, and query_tree_walker.

Referenced by find_dependent_phvs(), and find_dependent_phvs_in_jointree().

◆ find_jointree_node_for_rel()

static Node * find_jointree_node_for_rel ( Node jtnode,
int  relid 
)
static

Definition at line 4152 of file prepjointree.c.

4153 {
4154  if (jtnode == NULL)
4155  return NULL;
4156  if (IsA(jtnode, RangeTblRef))
4157  {
4158  int varno = ((RangeTblRef *) jtnode)->rtindex;
4159 
4160  if (relid == varno)
4161  return jtnode;
4162  }
4163  else if (IsA(jtnode, FromExpr))
4164  {
4165  FromExpr *f = (FromExpr *) jtnode;
4166  ListCell *l;
4167 
4168  foreach(l, f->fromlist)
4169  {
4170  jtnode = find_jointree_node_for_rel(lfirst(l), relid);
4171  if (jtnode)
4172  return jtnode;
4173  }
4174  }
4175  else if (IsA(jtnode, JoinExpr))
4176  {
4177  JoinExpr *j = (JoinExpr *) jtnode;
4178 
4179  if (relid == j->rtindex)
4180  return jtnode;
4181  jtnode = find_jointree_node_for_rel(j->larg, relid);
4182  if (jtnode)
4183  return jtnode;
4184  jtnode = find_jointree_node_for_rel(j->rarg, relid);
4185  if (jtnode)
4186  return jtnode;
4187  }
4188  else
4189  elog(ERROR, "unrecognized node type: %d",
4190  (int) nodeTag(jtnode));
4191  return NULL;
4192 }
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
int j
Definition: isn.c:73
#define nodeTag(nodeptr)
Definition: nodes.h:133
#define lfirst(lc)
Definition: pg_list.h:172
static Node * find_jointree_node_for_rel(Node *jtnode, int relid)
List * fromlist
Definition: primnodes.h:2308

References elog, ERROR, FromExpr::fromlist, IsA, j, lfirst, and nodeTag.

Referenced by get_relids_for_join().

◆ fix_append_rel_relids()

static void fix_append_rel_relids ( PlannerInfo root,
int  varno,
Relids  subrelids 
)
static

Definition at line 4030 of file prepjointree.c.

4031 {
4032  ListCell *l;
4033  int subvarno = -1;
4034 
4035  /*
4036  * We only want to extract the member relid once, but we mustn't fail
4037  * immediately if there are multiple members; it could be that none of the
4038  * AppendRelInfo nodes refer to it. So compute it on first use. Note that
4039  * bms_singleton_member will complain if set is not singleton.
4040  */
4041  foreach(l, root->append_rel_list)
4042  {
4043  AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
4044 
4045  /* The parent_relid shouldn't ever be a pullup target */
4046  Assert(appinfo->parent_relid != varno);
4047 
4048  if (appinfo->child_relid == varno)
4049  {
4050  if (subvarno < 0)
4051  subvarno = bms_singleton_member(subrelids);
4052  appinfo->child_relid = subvarno;
4053  }
4054 
4055  /* Also fix up any PHVs in its translated vars */
4056  if (root->glob->lastPHId != 0)
4058  varno, subrelids);
4059  }
4060 }
int bms_singleton_member(const Bitmapset *a)
Definition: bitmapset.c:672
static void substitute_phv_relids(Node *node, int varno, Relids subrelids)
Index child_relid
Definition: pathnodes.h:2980
List * translated_vars
Definition: pathnodes.h:3007
Index parent_relid
Definition: pathnodes.h:2979

References Assert, bms_singleton_member(), AppendRelInfo::child_relid, lfirst, AppendRelInfo::parent_relid, root, substitute_phv_relids(), and AppendRelInfo::translated_vars.

Referenced by pull_up_simple_subquery(), and remove_result_refs().

◆ flatten_simple_union_all()

void flatten_simple_union_all ( PlannerInfo root)

Definition at line 2815 of file prepjointree.c.

2816 {
2817  Query *parse = root->parse;
2818  SetOperationStmt *topop;
2819  Node *leftmostjtnode;
2820  int leftmostRTI;
2821  RangeTblEntry *leftmostRTE;
2822  int childRTI;
2823  RangeTblEntry *childRTE;
2824  RangeTblRef *rtr;
2825 
2826  /* Shouldn't be called unless query has setops */
2827  topop = castNode(SetOperationStmt, parse->setOperations);
2828  Assert(topop);
2829 
2830  /* Can't optimize away a recursive UNION */
2831  if (root->hasRecursion)
2832  return;
2833 
2834  /*
2835  * Recursively check the tree of set operations. If not all UNION ALL
2836  * with identical column types, punt.
2837  */
2838  if (!is_simple_union_all_recurse((Node *) topop, parse, topop->colTypes))
2839  return;
2840 
2841  /*
2842  * Locate the leftmost leaf query in the setops tree. The upper query's
2843  * Vars all refer to this RTE (see transformSetOperationStmt).
2844  */
2845  leftmostjtnode = topop->larg;
2846  while (leftmostjtnode && IsA(leftmostjtnode, SetOperationStmt))
2847  leftmostjtnode = ((SetOperationStmt *) leftmostjtnode)->larg;
2848  Assert(leftmostjtnode && IsA(leftmostjtnode, RangeTblRef));
2849  leftmostRTI = ((RangeTblRef *) leftmostjtnode)->rtindex;
2850  leftmostRTE = rt_fetch(leftmostRTI, parse->rtable);
2851  Assert(leftmostRTE->rtekind == RTE_SUBQUERY);
2852 
2853  /*
2854  * Make a copy of the leftmost RTE and add it to the rtable. This copy
2855  * will represent the leftmost leaf query in its capacity as a member of
2856  * the appendrel. The original will represent the appendrel as a whole.
2857  * (We must do things this way because the upper query's Vars have to be
2858  * seen as referring to the whole appendrel.)
2859  */
2860  childRTE = copyObject(leftmostRTE);
2861  parse->rtable = lappend(parse->rtable, childRTE);
2862  childRTI = list_length(parse->rtable);
2863 
2864  /* Modify the setops tree to reference the child copy */
2865  ((RangeTblRef *) leftmostjtnode)->rtindex = childRTI;
2866 
2867  /* Modify the formerly-leftmost RTE to mark it as an appendrel parent */
2868  leftmostRTE->inh = true;
2869 
2870  /*
2871  * Form a RangeTblRef for the appendrel, and insert it into FROM. The top
2872  * Query of a setops tree should have had an empty FromClause initially.
2873  */
2874  rtr = makeNode(RangeTblRef);
2875  rtr->rtindex = leftmostRTI;
2876  Assert(parse->jointree->fromlist == NIL);
2877  parse->jointree->fromlist = list_make1(rtr);
2878 
2879  /*
2880  * Now pretend the query has no setops. We must do this before trying to
2881  * do subquery pullup, because of Assert in pull_up_simple_subquery.
2882  */
2883  parse->setOperations = NULL;
2884 
2885  /*
2886  * Build AppendRelInfo information, and apply pull_up_subqueries to the
2887  * leaf queries of the UNION ALL. (We must do that now because they
2888  * weren't previously referenced by the jointree, and so were missed by
2889  * the main invocation of pull_up_subqueries.)
2890  */
2891  pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0);
2892 }
List * lappend(List *list, void *datum)
Definition: list.c:339
#define copyObject(obj)
Definition: nodes.h:224
#define makeNode(_type_)
Definition: nodes.h:155
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
@ RTE_SUBQUERY
Definition: parsenodes.h:1018
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, Query *setOpQuery, int childRToffset)
static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes)
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:717
RTEKind rtekind
Definition: parsenodes.h:1047

References Assert, castNode, copyObject, RangeTblEntry::inh, is_simple_union_all_recurse(), IsA, lappend(), SetOperationStmt::larg, list_length(), list_make1, makeNode, NIL, parse(), pull_up_union_leaf_queries(), root, rt_fetch, RTE_SUBQUERY, RangeTblEntry::rtekind, and RangeTblRef::rtindex.

Referenced by subquery_planner().

◆ get_nullingrels()

static nullingrel_info * get_nullingrels ( Query parse)
static

Definition at line 4201 of file prepjointree.c.

4202 {
4204 
4205  result->rtlength = list_length(parse->rtable);
4206  result->nullingrels = palloc0_array(Relids, result->rtlength + 1);
4207  get_nullingrels_recurse((Node *) parse->jointree, NULL, result);
4208  return result;
4209 }
#define palloc_object(type)
Definition: fe_memutils.h:74
#define palloc0_array(type, count)
Definition: fe_memutils.h:77
static void get_nullingrels_recurse(Node *jtnode, Relids upper_nullingrels, nullingrel_info *info)
Relids * nullingrels
Definition: prepjointree.c:51

References get_nullingrels_recurse(), list_length(), nullingrel_info::nullingrels, palloc0_array, palloc_object, parse(), and nullingrel_info::rtlength.

Referenced by pull_up_simple_subquery().

◆ get_nullingrels_recurse()

static void get_nullingrels_recurse ( Node jtnode,
Relids  upper_nullingrels,
nullingrel_info info 
)
static

Definition at line 4221 of file prepjointree.c.

4223 {
4224  if (jtnode == NULL)
4225  return;
4226  if (IsA(jtnode, RangeTblRef))
4227  {
4228  int varno = ((RangeTblRef *) jtnode)->rtindex;
4229 
4230  Assert(varno > 0 && varno <= info->rtlength);
4231  info->nullingrels[varno] = upper_nullingrels;
4232  }
4233  else if (IsA(jtnode, FromExpr))
4234  {
4235  FromExpr *f = (FromExpr *) jtnode;
4236  ListCell *l;
4237 
4238  foreach(l, f->fromlist)
4239  {
4240  get_nullingrels_recurse(lfirst(l), upper_nullingrels, info);
4241  }
4242  }
4243  else if (IsA(jtnode, JoinExpr))
4244  {
4245  JoinExpr *j = (JoinExpr *) jtnode;
4246  Relids local_nullingrels;
4247 
4248  switch (j->jointype)
4249  {
4250  case JOIN_INNER:
4251  get_nullingrels_recurse(j->larg, upper_nullingrels, info);
4252  get_nullingrels_recurse(j->rarg, upper_nullingrels, info);
4253  break;
4254  case JOIN_LEFT:
4255  case JOIN_SEMI:
4256  case JOIN_ANTI:
4257  local_nullingrels = bms_add_member(bms_copy(upper_nullingrels),
4258  j->rtindex);
4259  get_nullingrels_recurse(j->larg, upper_nullingrels, info);
4260  get_nullingrels_recurse(j->rarg, local_nullingrels, info);
4261  break;
4262  case JOIN_FULL:
4263  local_nullingrels = bms_add_member(bms_copy(upper_nullingrels),
4264  j->rtindex);
4265  get_nullingrels_recurse(j->larg, local_nullingrels, info);
4266  get_nullingrels_recurse(j->rarg, local_nullingrels, info);
4267  break;
4268  case JOIN_RIGHT:
4269  local_nullingrels = bms_add_member(bms_copy(upper_nullingrels),
4270  j->rtindex);
4271  get_nullingrels_recurse(j->larg, local_nullingrels, info);
4272  get_nullingrels_recurse(j->rarg, upper_nullingrels, info);
4273  break;
4274  default:
4275  elog(ERROR, "unrecognized join type: %d",
4276  (int) j->jointype);
4277  break;
4278  }
4279  }
4280  else
4281  elog(ERROR, "unrecognized node type: %d",
4282  (int) nodeTag(jtnode));
4283 }
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:122
@ JOIN_SEMI
Definition: nodes.h:307
@ JOIN_FULL
Definition: nodes.h:295
@ JOIN_INNER
Definition: nodes.h:293
@ JOIN_RIGHT
Definition: nodes.h:296
@ JOIN_LEFT
Definition: nodes.h:294
@ JOIN_ANTI
Definition: nodes.h:308

References Assert, bms_add_member(), bms_copy(), elog, ERROR, FromExpr::fromlist, IsA, j, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, lfirst, nodeTag, and nullingrel_info::nullingrels.

Referenced by get_nullingrels().

◆ get_relids_for_join()

Relids get_relids_for_join ( Query query,
int  joinrelid 
)

Definition at line 4135 of file prepjointree.c.

4136 {
4137  Node *jtnode;
4138 
4139  jtnode = find_jointree_node_for_rel((Node *) query->jointree,
4140  joinrelid);
4141  if (!jtnode)
4142  elog(ERROR, "could not find join node %d", joinrelid);
4143  return get_relids_in_jointree(jtnode, true, false);
4144 }
FromExpr * jointree
Definition: parsenodes.h:177

References elog, ERROR, find_jointree_node_for_rel(), get_relids_in_jointree(), and Query::jointree.

Referenced by add_nullingrels_if_needed(), and alias_relid_set().

◆ get_relids_in_jointree()

Relids get_relids_in_jointree ( Node jtnode,
bool  include_outer_joins,
bool  include_inner_joins 
)

Definition at line 4074 of file prepjointree.c.

4076 {
4077  Relids result = NULL;
4078 
4079  if (jtnode == NULL)
4080  return result;
4081  if (IsA(jtnode, RangeTblRef))
4082  {
4083  int varno = ((RangeTblRef *) jtnode)->rtindex;
4084 
4085  result = bms_make_singleton(varno);
4086  }
4087  else if (IsA(jtnode, FromExpr))
4088  {
4089  FromExpr *f = (FromExpr *) jtnode;
4090  ListCell *l;
4091 
4092  foreach(l, f->fromlist)
4093  {
4094  result = bms_join(result,
4096  include_outer_joins,
4097  include_inner_joins));
4098  }
4099  }
4100  else if (IsA(jtnode, JoinExpr))
4101  {
4102  JoinExpr *j = (JoinExpr *) jtnode;
4103 
4104  result = get_relids_in_jointree(j->larg,
4105  include_outer_joins,
4106  include_inner_joins);
4107  result = bms_join(result,
4108  get_relids_in_jointree(j->rarg,
4109  include_outer_joins,
4110  include_inner_joins));
4111  if (j->rtindex)
4112  {
4113  if (j->jointype == JOIN_INNER)
4114  {
4115  if (include_inner_joins)
4116  result = bms_add_member(result, j->rtindex);
4117  }
4118  else
4119  {
4120  if (include_outer_joins)
4121  result = bms_add_member(result, j->rtindex);
4122  }
4123  }
4124  }
4125  else
4126  elog(ERROR, "unrecognized node type: %d",
4127  (int) nodeTag(jtnode));
4128  return result;
4129 }
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:1230

References bms_add_member(), bms_join(), bms_make_singleton(), elog, ERROR, FromExpr::fromlist, IsA, j, JOIN_INNER, lfirst, and nodeTag.

Referenced by find_dependent_phvs_in_jointree(), get_relids_for_join(), is_simple_subquery(), mark_nullable_by_grouping(), preprocess_rowmarks(), pull_up_simple_subquery(), and remove_result_refs().

◆ get_result_relid()

static int get_result_relid ( PlannerInfo root,
Node jtnode 
)
static

Definition at line 3772 of file prepjointree.c.

3773 {
3774  int varno;
3775 
3776  if (!IsA(jtnode, RangeTblRef))
3777  return 0;
3778  varno = ((RangeTblRef *) jtnode)->rtindex;
3779  if (rt_fetch(varno, root->parse->rtable)->rtekind != RTE_RESULT)
3780  return 0;
3781  return varno;
3782 }
@ RTE_RESULT
Definition: parsenodes.h:1025

References IsA, root, rt_fetch, and RTE_RESULT.

Referenced by remove_useless_results_recurse().

◆ is_safe_append_member()

static bool is_safe_append_member ( Query subquery)
static

Definition at line 2138 of file prepjointree.c.

2139 {
2140  FromExpr *jtnode;
2141 
2142  /*
2143  * It's only safe to pull up the child if its jointree contains exactly
2144  * one RTE, else the AppendRelInfo data structure breaks. The one base RTE
2145  * could be buried in several levels of FromExpr, however. Also, if the
2146  * child's jointree is completely empty, we can pull up because
2147  * pull_up_simple_subquery will insert a single RTE_RESULT RTE instead.
2148  *
2149  * Also, the child can't have any WHERE quals because there's no place to
2150  * put them in an appendrel. (This is a bit annoying...) If we didn't
2151  * need to check this, we'd just test whether get_relids_in_jointree()
2152  * yields a singleton set, to be more consistent with the coding of
2153  * fix_append_rel_relids().
2154  */
2155  jtnode = subquery->jointree;
2156  Assert(IsA(jtnode, FromExpr));
2157  /* Check the completely-empty case */
2158  if (jtnode->fromlist == NIL && jtnode->quals == NULL)
2159  return true;
2160  /* Check the more general case */
2161  while (IsA(jtnode, FromExpr))
2162  {
2163  if (jtnode->quals != NULL)
2164  return false;
2165  if (list_length(jtnode->fromlist) != 1)
2166  return false;
2167  jtnode = linitial(jtnode->fromlist);
2168  }
2169  if (!IsA(jtnode, RangeTblRef))
2170  return false;
2171 
2172  return true;
2173 }
#define linitial(l)
Definition: pg_list.h:178
Node * quals
Definition: primnodes.h:2309

References Assert, FromExpr::fromlist, IsA, Query::jointree, linitial, list_length(), NIL, and FromExpr::quals.

Referenced by pull_up_simple_subquery(), and pull_up_subqueries_recurse().

◆ is_simple_subquery()

static bool is_simple_subquery ( PlannerInfo root,
Query subquery,
RangeTblEntry rte,
JoinExpr lowest_outer_join 
)
static

Definition at line 1654 of file prepjointree.c.

1656 {
1657  /*
1658  * Let's just make sure it's a valid subselect ...
1659  */
1660  if (!IsA(subquery, Query) ||
1661  subquery->commandType != CMD_SELECT)
1662  elog(ERROR, "subquery is bogus");
1663 
1664  /*
1665  * Can't currently pull up a query with setops (unless it's simple UNION
1666  * ALL, which is handled by a different code path). Maybe after querytree
1667  * redesign...
1668  */
1669  if (subquery->setOperations)
1670  return false;
1671 
1672  /*
1673  * Can't pull up a subquery involving grouping, aggregation, SRFs,
1674  * sorting, limiting, or WITH. (XXX WITH could possibly be allowed later)
1675  *
1676  * We also don't pull up a subquery that has explicit FOR UPDATE/SHARE
1677  * clauses, because pullup would cause the locking to occur semantically
1678  * higher than it should. Implicit FOR UPDATE/SHARE is okay because in
1679  * that case the locking was originally declared in the upper query
1680  * anyway.
1681  */
1682  if (subquery->hasAggs ||
1683  subquery->hasWindowFuncs ||
1684  subquery->hasTargetSRFs ||
1685  subquery->groupClause ||
1686  subquery->groupingSets ||
1687  subquery->havingQual ||
1688  subquery->sortClause ||
1689  subquery->distinctClause ||
1690  subquery->limitOffset ||
1691  subquery->limitCount ||
1692  subquery->hasForUpdate ||
1693  subquery->cteList)
1694  return false;
1695 
1696  /*
1697  * Don't pull up if the RTE represents a security-barrier view; we
1698  * couldn't prevent information leakage once the RTE's Vars are scattered
1699  * about in the upper query.
1700  */
1701  if (rte->security_barrier)
1702  return false;
1703 
1704  /*
1705  * If the subquery is LATERAL, check for pullup restrictions from that.
1706  */
1707  if (rte->lateral)
1708  {
1709  bool restricted;
1710  Relids safe_upper_varnos;
1711 
1712  /*
1713  * The subquery's WHERE and JOIN/ON quals mustn't contain any lateral
1714  * references to rels outside a higher outer join (including the case
1715  * where the outer join is within the subquery itself). In such a
1716  * case, pulling up would result in a situation where we need to
1717  * postpone quals from below an outer join to above it, which is
1718  * probably completely wrong and in any case is a complication that
1719  * doesn't seem worth addressing at the moment.
1720  */
1721  if (lowest_outer_join != NULL)
1722  {
1723  restricted = true;
1724  safe_upper_varnos = get_relids_in_jointree((Node *) lowest_outer_join,
1725  true, true);
1726  }
1727  else
1728  {
1729  restricted = false;
1730  safe_upper_varnos = NULL; /* doesn't matter */
1731  }
1732 
1734  (Node *) subquery->jointree,
1735  restricted, safe_upper_varnos))
1736  return false;
1737 
1738  /*
1739  * If there's an outer join above the LATERAL subquery, also disallow
1740  * pullup if the subquery's targetlist has any references to rels
1741  * outside the outer join, since these might get pulled into quals
1742  * above the subquery (but in or below the outer join) and then lead
1743  * to qual-postponement issues similar to the case checked for above.
1744  * (We wouldn't need to prevent pullup if no such references appear in
1745  * outer-query quals, but we don't have enough info here to check
1746  * that. Also, maybe this restriction could be removed if we forced
1747  * such refs to be wrapped in PlaceHolderVars, even when they're below
1748  * the nearest outer join? But it's a pretty hokey usage, so not
1749  * clear this is worth sweating over.)
1750  *
1751  * If you change this, see also the comments about lateral references
1752  * in pullup_replace_vars_callback().
1753  */
1754  if (lowest_outer_join != NULL)
1755  {
1756  Relids lvarnos = pull_varnos_of_level(root,
1757  (Node *) subquery->targetList,
1758  1);
1759 
1760  if (!bms_is_subset(lvarnos, safe_upper_varnos))
1761  return false;
1762  }
1763  }
1764 
1765  /*
1766  * Don't pull up a subquery that has any volatile functions in its
1767  * targetlist. Otherwise we might introduce multiple evaluations of these
1768  * functions, if they get copied to multiple places in the upper query,
1769  * leading to surprising results. (Note: the PlaceHolderVar mechanism
1770  * doesn't quite guarantee single evaluation; else we could pull up anyway
1771  * and just wrap such items in PlaceHolderVars ...)
1772  */
1773  if (contain_volatile_functions((Node *) subquery->targetList))
1774  return false;
1775 
1776  return true;
1777 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:412
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:537
@ CMD_SELECT
Definition: nodes.h:265
static bool jointree_contains_lateral_outer_refs(PlannerInfo *root, Node *jtnode, bool restricted, Relids safe_upper_varnos)
Node * limitCount
Definition: parsenodes.h:216
Node * setOperations
Definition: parsenodes.h:221
List * cteList
Definition: parsenodes.h:168
List * groupClause
Definition: parsenodes.h:202
Node * havingQual
Definition: parsenodes.h:207
Node * limitOffset
Definition: parsenodes.h:215
CmdType commandType
Definition: parsenodes.h:121
List * targetList
Definition: parsenodes.h:193
List * groupingSets
Definition: parsenodes.h:205
List * distinctClause
Definition: parsenodes.h:211
List * sortClause
Definition: parsenodes.h:213
Relids pull_varnos_of_level(PlannerInfo *root, Node *node, int levelsup)
Definition: var.c:139

References bms_is_subset(), CMD_SELECT, Query::commandType, contain_volatile_functions(), Query::cteList, Query::distinctClause, elog, ERROR, get_relids_in_jointree(), Query::groupClause, Query::groupingSets, Query::havingQual, IsA, Query::jointree, jointree_contains_lateral_outer_refs(), Query::limitCount, Query::limitOffset, pull_varnos_of_level(), root, Query::setOperations, Query::sortClause, and Query::targetList.

Referenced by pull_up_simple_subquery(), and pull_up_subqueries_recurse().

◆ is_simple_union_all()

static bool is_simple_union_all ( Query subquery)
static

Definition at line 2067 of file prepjointree.c.

2068 {
2069  SetOperationStmt *topop;
2070 
2071  /* Let's just make sure it's a valid subselect ... */
2072  if (!IsA(subquery, Query) ||
2073  subquery->commandType != CMD_SELECT)
2074  elog(ERROR, "subquery is bogus");
2075 
2076  /* Is it a set-operation query at all? */
2077  topop = castNode(SetOperationStmt, subquery->setOperations);
2078  if (!topop)
2079  return false;
2080 
2081  /* Can't handle ORDER BY, LIMIT/OFFSET, locking, or WITH */
2082  if (subquery->sortClause ||
2083  subquery->limitOffset ||
2084  subquery->limitCount ||
2085  subquery->rowMarks ||
2086  subquery->cteList)
2087  return false;
2088 
2089  /* Recursively check the tree of set operations */
2090  return is_simple_union_all_recurse((Node *) topop, subquery,
2091  topop->colTypes);
2092 }
List * rowMarks
Definition: parsenodes.h:219

References castNode, CMD_SELECT, Query::commandType, Query::cteList, elog, ERROR, is_simple_union_all_recurse(), IsA, Query::limitCount, Query::limitOffset, Query::rowMarks, Query::setOperations, and Query::sortClause.

Referenced by pull_up_subqueries_recurse().

◆ is_simple_union_all_recurse()

static bool is_simple_union_all_recurse ( Node setOp,
Query setOpQuery,
List colTypes 
)
static

Definition at line 2095 of file prepjointree.c.

2096 {
2097  /* Since this function recurses, it could be driven to stack overflow. */
2099 
2100  if (IsA(setOp, RangeTblRef))
2101  {
2102  RangeTblRef *rtr = (RangeTblRef *) setOp;
2103  RangeTblEntry *rte = rt_fetch(rtr->rtindex, setOpQuery->rtable);
2104  Query *subquery = rte->subquery;
2105 
2106  Assert(subquery != NULL);
2107 
2108  /* Leaf nodes are OK if they match the toplevel column types */
2109  /* We don't have to compare typmods or collations here */
2110  return tlist_same_datatypes(subquery->targetList, colTypes, true);
2111  }
2112  else if (IsA(setOp, SetOperationStmt))
2113  {
2114  SetOperationStmt *op = (SetOperationStmt *) setOp;
2115 
2116  /* Must be UNION ALL */
2117  if (op->op != SETOP_UNION || !op->all)
2118  return false;
2119 
2120  /* Recurse to check inputs */
2121  return is_simple_union_all_recurse(op->larg, setOpQuery, colTypes) &&
2122  is_simple_union_all_recurse(op->rarg, setOpQuery, colTypes);
2123  }
2124  else
2125  {
2126  elog(ERROR, "unrecognized node type: %d",
2127  (int) nodeTag(setOp));
2128  return false; /* keep compiler quiet */
2129  }
2130 }
@ SETOP_UNION
Definition: parsenodes.h:2119
void check_stack_depth(void)
Definition: postgres.c:3574
List * rtable
Definition: parsenodes.h:170
Query * subquery
Definition: parsenodes.h:1104
SetOperation op
Definition: parsenodes.h:2198
bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
Definition: tlist.c:248

References SetOperationStmt::all, Assert, check_stack_depth(), elog, ERROR, IsA, SetOperationStmt::larg, nodeTag, SetOperationStmt::op, SetOperationStmt::rarg, rt_fetch, Query::rtable, RangeTblRef::rtindex, SETOP_UNION, RangeTblEntry::subquery, Query::targetList, and tlist_same_datatypes().

Referenced by flatten_simple_union_all(), and is_simple_union_all().

◆ is_simple_values()

static bool is_simple_values ( PlannerInfo root,
RangeTblEntry rte 
)
static

Definition at line 1890 of file prepjointree.c.

1891 {
1892  Assert(rte->rtekind == RTE_VALUES);
1893 
1894  /*
1895  * There must be exactly one VALUES list, else it's not semantically
1896  * correct to replace the VALUES RTE with a RESULT RTE, nor would we have
1897  * a unique set of expressions to substitute into the parent query.
1898  */
1899  if (list_length(rte->values_lists) != 1)
1900  return false;
1901 
1902  /*
1903  * Because VALUES can't appear under an outer join (or at least, we won't
1904  * try to pull it up if it does), we need not worry about LATERAL, nor
1905  * about validity of PHVs for the VALUES' outputs.
1906  */
1907 
1908  /*
1909  * Don't pull up a VALUES that contains any set-returning or volatile
1910  * functions. The considerations here are basically identical to the
1911  * restrictions on a pull-able subquery's targetlist.
1912  */
1913  if (expression_returns_set((Node *) rte->values_lists) ||
1915  return false;
1916 
1917  /*
1918  * Do not pull up a VALUES that's not the only RTE in its parent query.
1919  * This is actually the only case that the parser will generate at the
1920  * moment, and assuming this is true greatly simplifies
1921  * pull_up_simple_values().
1922  */
1923  if (list_length(root->parse->rtable) != 1 ||
1924  rte != (RangeTblEntry *) linitial(root->parse->rtable))
1925  return false;
1926 
1927  return true;
1928 }
bool expression_returns_set(Node *clause)
Definition: nodeFuncs.c:758
@ RTE_VALUES
Definition: parsenodes.h:1022
List * values_lists
Definition: parsenodes.h:1190

References Assert, contain_volatile_functions(), expression_returns_set(), linitial, list_length(), root, RTE_VALUES, RangeTblEntry::rtekind, and RangeTblEntry::values_lists.

Referenced by pull_up_subqueries_recurse().

◆ jointree_contains_lateral_outer_refs()

static bool jointree_contains_lateral_outer_refs ( PlannerInfo root,
Node jtnode,
bool  restricted,
Relids  safe_upper_varnos 
)
static

Definition at line 2186 of file prepjointree.c.

2189 {
2190  if (jtnode == NULL)
2191  return false;
2192  if (IsA(jtnode, RangeTblRef))
2193  return false;
2194  else if (IsA(jtnode, FromExpr))
2195  {
2196  FromExpr *f = (FromExpr *) jtnode;
2197  ListCell *l;
2198 
2199  /* First, recurse to check child joins */
2200  foreach(l, f->fromlist)
2201  {
2203  lfirst(l),
2204  restricted,
2205  safe_upper_varnos))
2206  return true;
2207  }
2208 
2209  /* Then check the top-level quals */
2210  if (restricted &&
2212  safe_upper_varnos))
2213  return true;
2214  }
2215  else if (IsA(jtnode, JoinExpr))
2216  {
2217  JoinExpr *j = (JoinExpr *) jtnode;
2218 
2219  /*
2220  * If this is an outer join, we mustn't allow any upper lateral
2221  * references in or below it.
2222  */
2223  if (j->jointype != JOIN_INNER)
2224  {
2225  restricted = true;
2226  safe_upper_varnos = NULL;
2227  }
2228 
2229  /* Check the child joins */
2231  j->larg,
2232  restricted,
2233  safe_upper_varnos))
2234  return true;
2236  j->rarg,
2237  restricted,
2238  safe_upper_varnos))
2239  return true;
2240 
2241  /* Check the JOIN's qual clauses */
2242  if (restricted &&
2244  safe_upper_varnos))
2245  return true;
2246  }
2247  else
2248  elog(ERROR, "unrecognized node type: %d",
2249  (int) nodeTag(jtnode));
2250  return false;
2251 }

References bms_is_subset(), elog, ERROR, FromExpr::fromlist, IsA, j, JOIN_INNER, lfirst, nodeTag, pull_varnos_of_level(), FromExpr::quals, and root.

Referenced by is_simple_subquery().

◆ make_setop_translation_list()

static void make_setop_translation_list ( Query query,
int  newvarno,
AppendRelInfo appinfo 
)
static

Definition at line 1616 of file prepjointree.c.

1618 {
1619  List *vars = NIL;
1620  AttrNumber *pcolnos;
1621  ListCell *l;
1622 
1623  /* Initialize reverse-translation array with all entries zero */
1624  /* (entries for resjunk columns will stay that way) */
1625  appinfo->num_child_cols = list_length(query->targetList);
1626  appinfo->parent_colnos = pcolnos =
1627  (AttrNumber *) palloc0(appinfo->num_child_cols * sizeof(AttrNumber));
1628 
1629  foreach(l, query->targetList)
1630  {
1631  TargetEntry *tle = (TargetEntry *) lfirst(l);
1632 
1633  if (tle->resjunk)
1634  continue;
1635 
1636  vars = lappend(vars, makeVarFromTargetEntry(newvarno, tle));
1637  pcolnos[tle->resno - 1] = tle->resno;
1638  }
1639 
1640  appinfo->translated_vars = vars;
1641 }
int16 AttrNumber
Definition: attnum.h:21
Var * makeVarFromTargetEntry(int varno, TargetEntry *tle)
Definition: makefuncs.c:105
void * palloc0(Size size)
Definition: mcxt.c:1347
int num_child_cols
Definition: pathnodes.h:3015
Definition: pg_list.h:54
AttrNumber resno
Definition: primnodes.h:2192
Definition: regcomp.c:282

References lappend(), lfirst, list_length(), makeVarFromTargetEntry(), NIL, AppendRelInfo::num_child_cols, palloc0(), TargetEntry::resno, Query::targetList, and AppendRelInfo::translated_vars.

Referenced by pull_up_union_leaf_queries().

◆ perform_pullup_replace_vars()

static void perform_pullup_replace_vars ( PlannerInfo root,
pullup_replace_vars_context rvcontext,
AppendRelInfo containing_appendrel 
)
static

Definition at line 2261 of file prepjointree.c.

2264 {
2265  Query *parse = root->parse;
2266  ListCell *lc;
2267 
2268  /*
2269  * If we are considering an appendrel child subquery (that is, a UNION ALL
2270  * member query that we're pulling up), then the only part of the upper
2271  * query that could reference the child yet is the translated_vars list of
2272  * the associated AppendRelInfo. Furthermore, we do not want to force use
2273  * of PHVs in the AppendRelInfo --- there isn't any outer join between.
2274  */
2275  if (containing_appendrel)
2276  {
2277  bool save_wrap_non_vars = rvcontext->wrap_non_vars;
2278 
2279  rvcontext->wrap_non_vars = false;
2280  containing_appendrel->translated_vars = (List *)
2281  pullup_replace_vars((Node *) containing_appendrel->translated_vars,
2282  rvcontext);
2283  rvcontext->wrap_non_vars = save_wrap_non_vars;
2284  return;
2285  }
2286 
2287  /*
2288  * Replace all of the top query's references to the subquery's outputs
2289  * with copies of the adjusted subtlist items, being careful not to
2290  * replace any of the jointree structure. (This'd be a lot cleaner if we
2291  * could use query_tree_mutator.) We have to use PHVs in the targetList,
2292  * returningList, and havingQual, since those are certainly above any
2293  * outer join. replace_vars_in_jointree tracks its location in the
2294  * jointree and uses PHVs or not appropriately.
2295  */
2296  parse->targetList = (List *)
2297  pullup_replace_vars((Node *) parse->targetList, rvcontext);
2298  parse->returningList = (List *)
2299  pullup_replace_vars((Node *) parse->returningList, rvcontext);
2300 
2301  if (parse->onConflict)
2302  {
2303  parse->onConflict->onConflictSet = (List *)
2304  pullup_replace_vars((Node *) parse->onConflict->onConflictSet,
2305  rvcontext);
2306  parse->onConflict->onConflictWhere =
2307  pullup_replace_vars(parse->onConflict->onConflictWhere,
2308  rvcontext);
2309 
2310  /*
2311  * We assume ON CONFLICT's arbiterElems, arbiterWhere, exclRelTlist
2312  * can't contain any references to a subquery.
2313  */
2314  }
2315  if (parse->mergeActionList)
2316  {
2317  foreach(lc, parse->mergeActionList)
2318  {
2319  MergeAction *action = lfirst(lc);
2320 
2321  action->qual = pullup_replace_vars(action->qual, rvcontext);
2322  action->targetList = (List *)
2323  pullup_replace_vars((Node *) action->targetList, rvcontext);
2324  }
2325  }
2326  parse->mergeJoinCondition = pullup_replace_vars(parse->mergeJoinCondition,
2327  rvcontext);
2328  replace_vars_in_jointree((Node *) parse->jointree, rvcontext);
2329  Assert(parse->setOperations == NULL);
2330  parse->havingQual = pullup_replace_vars(parse->havingQual, rvcontext);
2331 
2332  /*
2333  * Replace references in the translated_vars lists of appendrels.
2334  */
2335  foreach(lc, root->append_rel_list)
2336  {
2337  AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
2338 
2339  appinfo->translated_vars = (List *)
2340  pullup_replace_vars((Node *) appinfo->translated_vars, rvcontext);
2341  }
2342 
2343  /*
2344  * Replace references in the joinaliasvars lists of join RTEs and the
2345  * groupexprs list of group RTE.
2346  */
2347  foreach(lc, parse->rtable)
2348  {
2349  RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(lc);
2350 
2351  if (otherrte->rtekind == RTE_JOIN)
2352  otherrte->joinaliasvars = (List *)
2353  pullup_replace_vars((Node *) otherrte->joinaliasvars,
2354  rvcontext);
2355  else if (otherrte->rtekind == RTE_GROUP)
2356  otherrte->groupexprs = (List *)
2357  pullup_replace_vars((Node *) otherrte->groupexprs,
2358  rvcontext);
2359  }
2360 }
@ RTE_JOIN
Definition: parsenodes.h:1019
@ RTE_GROUP
Definition: parsenodes.h:1028
static void replace_vars_in_jointree(Node *jtnode, pullup_replace_vars_context *context)
static Node * pullup_replace_vars(Node *expr, pullup_replace_vars_context *context)

References generate_unaccent_rules::action, Assert, lfirst, parse(), pullup_replace_vars(), replace_vars_in_jointree(), root, RTE_GROUP, RTE_JOIN, RangeTblEntry::rtekind, AppendRelInfo::translated_vars, and pullup_replace_vars_context::wrap_non_vars.

Referenced by pull_up_constant_function(), pull_up_simple_subquery(), and pull_up_simple_values().

◆ preprocess_function_rtes()

void preprocess_function_rtes ( PlannerInfo root)

Definition at line 887 of file prepjointree.c.

888 {
889  ListCell *rt;
890 
891  foreach(rt, root->parse->rtable)
892  {
893  RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
894 
895  if (rte->rtekind == RTE_FUNCTION)
896  {
897  Query *funcquery;
898 
899  /* Apply const-simplification */
900  rte->functions = (List *)
902 
903  /* Check safety of expansion, and expand if possible */
904  funcquery = inline_set_returning_function(root, rte);
905  if (funcquery)
906  {
907  /* Successful expansion, convert the RTE to a subquery */
908  rte->rtekind = RTE_SUBQUERY;
909  rte->subquery = funcquery;
910  rte->security_barrier = false;
911  /* Clear fields that should not be set in a subquery RTE */
912  rte->functions = NIL;
913  rte->funcordinality = false;
914  }
915  }
916  }
917 }
Query * inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
Definition: clauses.c:5063
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2253
@ RTE_FUNCTION
Definition: parsenodes.h:1020
bool funcordinality
Definition: parsenodes.h:1179
List * functions
Definition: parsenodes.h:1177

References eval_const_expressions(), RangeTblEntry::funcordinality, RangeTblEntry::functions, inline_set_returning_function(), lfirst, NIL, root, RTE_FUNCTION, RTE_SUBQUERY, RangeTblEntry::rtekind, and RangeTblEntry::subquery.

Referenced by pull_up_simple_subquery(), and subquery_planner().

◆ pull_up_constant_function()

static Node * pull_up_constant_function ( PlannerInfo root,
Node jtnode,
RangeTblEntry rte,
AppendRelInfo containing_appendrel 
)
static

Definition at line 1949 of file prepjointree.c.

1952 {
1953  Query *parse = root->parse;
1954  RangeTblFunction *rtf;
1955  TypeFuncClass functypclass;
1956  Oid funcrettype;
1957  TupleDesc tupdesc;
1958  pullup_replace_vars_context rvcontext;
1959 
1960  /* Fail if the RTE has ORDINALITY - we don't implement that here. */
1961  if (rte->funcordinality)
1962  return jtnode;
1963 
1964  /* Fail if RTE isn't a single, simple Const expr */
1965  if (list_length(rte->functions) != 1)
1966  return jtnode;
1968  if (!IsA(rtf->funcexpr, Const))
1969  return jtnode;
1970 
1971  /*
1972  * If the function's result is not a scalar, we punt. In principle we
1973  * could break the composite constant value apart into per-column
1974  * constants, but for now it seems not worth the work.
1975  */
1976  if (rtf->funccolcount != 1)
1977  return jtnode; /* definitely composite */
1978 
1979  /* If it has a coldeflist, it certainly returns RECORD */
1980  if (rtf->funccolnames != NIL)
1981  return jtnode; /* must be a one-column RECORD type */
1982 
1983  functypclass = get_expr_result_type(rtf->funcexpr,
1984  &funcrettype,
1985  &tupdesc);
1986  if (functypclass != TYPEFUNC_SCALAR)
1987  return jtnode; /* must be a one-column composite type */
1988 
1989  /* Create context for applying pullup_replace_vars */
1990  rvcontext.root = root;
1991  rvcontext.targetlist = list_make1(makeTargetEntry((Expr *) rtf->funcexpr,
1992  1, /* resno */
1993  NULL, /* resname */
1994  false)); /* resjunk */
1995  rvcontext.target_rte = rte;
1996 
1997  /*
1998  * Since this function was reduced to a Const, it doesn't contain any
1999  * lateral references, even if it's marked as LATERAL. This means we
2000  * don't need to fill relids or nullinfo.
2001  */
2002  rvcontext.relids = NULL;
2003  rvcontext.nullinfo = NULL;
2004 
2005  rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
2006  rvcontext.varno = ((RangeTblRef *) jtnode)->rtindex;
2007  /* this flag will be set below, if needed */
2008  rvcontext.wrap_non_vars = false;
2009  /* initialize cache array with indexes 0 .. length(tlist) */
2010  rvcontext.rv_cache = palloc0((list_length(rvcontext.targetlist) + 1) *
2011  sizeof(Node *));
2012 
2013  /*
2014  * If we are dealing with an appendrel member then anything that's not a
2015  * simple Var has to be turned into a PlaceHolderVar. (See comments in
2016  * pull_up_simple_subquery().)
2017  */
2018  if (containing_appendrel != NULL)
2019  rvcontext.wrap_non_vars = true;
2020 
2021  /*
2022  * If the parent query uses grouping sets, we need a PlaceHolderVar for
2023  * anything that's not a simple Var.
2024  */
2025  if (parse->groupingSets)
2026  rvcontext.wrap_non_vars = true;
2027 
2028  /*
2029  * Replace all of the top query's references to the RTE's output with
2030  * copies of the funcexpr, being careful not to replace any of the
2031  * jointree structure.
2032  */
2033  perform_pullup_replace_vars(root, &rvcontext,
2034  containing_appendrel);
2035 
2036  /*
2037  * We don't need to bother with changing PlaceHolderVars in the parent
2038  * query. Their references to the RT index are still good for now, and
2039  * will get removed later if we're able to drop the RTE_RESULT.
2040  */
2041 
2042  /*
2043  * Convert the RTE to be RTE_RESULT type, signifying that we don't need to
2044  * scan it anymore, and zero out RTE_FUNCTION-specific fields. Also make
2045  * sure the RTE is not marked LATERAL, since elsewhere we don't expect
2046  * RTE_RESULTs to be LATERAL.
2047  */
2048  rte->rtekind = RTE_RESULT;
2049  rte->functions = NIL;
2050  rte->lateral = false;
2051 
2052  /*
2053  * We can reuse the RangeTblRef node.
2054  */
2055  return jtnode;
2056 }
TypeFuncClass get_expr_result_type(Node *expr, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:299
TypeFuncClass
Definition: funcapi.h:147
@ TYPEFUNC_SCALAR
Definition: funcapi.h:148
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:240
#define linitial_node(type, l)
Definition: pg_list.h:181
unsigned int Oid
Definition: postgres_ext.h:31
static void perform_pullup_replace_vars(PlannerInfo *root, pullup_replace_vars_context *rvcontext, AppendRelInfo *containing_appendrel)
nullingrel_info * nullinfo
Definition: prepjointree.c:63
RangeTblEntry * target_rte
Definition: prepjointree.c:60

References RangeTblFunction::funcexpr, RangeTblEntry::funcordinality, RangeTblEntry::functions, get_expr_result_type(), IsA, linitial_node, list_length(), list_make1, makeTargetEntry(), NIL, pullup_replace_vars_context::nullinfo, pullup_replace_vars_context::outer_hasSubLinks, palloc0(), parse(), perform_pullup_replace_vars(), pullup_replace_vars_context::relids, pullup_replace_vars_context::root, root, RTE_RESULT, RangeTblEntry::rtekind, pullup_replace_vars_context::rv_cache, pullup_replace_vars_context::target_rte, pullup_replace_vars_context::targetlist, TYPEFUNC_SCALAR, pullup_replace_vars_context::varno, and pullup_replace_vars_context::wrap_non_vars.

Referenced by pull_up_subqueries_recurse().

◆ pull_up_simple_subquery()

static Node * pull_up_simple_subquery ( PlannerInfo root,
Node jtnode,
RangeTblEntry rte,
JoinExpr lowest_outer_join,
AppendRelInfo containing_appendrel 
)
static

Definition at line 1117 of file prepjointree.c.

1120 {
1121  Query *parse = root->parse;
1122  int varno = ((RangeTblRef *) jtnode)->rtindex;
1123  Query *subquery;
1124  PlannerInfo *subroot;
1125  int rtoffset;
1126  pullup_replace_vars_context rvcontext;
1127  ListCell *lc;
1128 
1129  /*
1130  * Make a modifiable copy of the subquery to hack on, so that the RTE will
1131  * be left unchanged in case we decide below that we can't pull it up
1132  * after all.
1133  */
1134  subquery = copyObject(rte->subquery);
1135 
1136  /*
1137  * Create a PlannerInfo data structure for this subquery.
1138  *
1139  * NOTE: the next few steps should match the first processing in
1140  * subquery_planner(). Can we refactor to avoid code duplication, or
1141  * would that just make things uglier?
1142  */
1143  subroot = makeNode(PlannerInfo);
1144  subroot->parse = subquery;
1145  subroot->glob = root->glob;
1146  subroot->query_level = root->query_level;
1147  subroot->parent_root = root->parent_root;
1148  subroot->plan_params = NIL;
1149  subroot->outer_params = NULL;
1150  subroot->planner_cxt = CurrentMemoryContext;
1151  subroot->init_plans = NIL;
1152  subroot->cte_plan_ids = NIL;
1153  subroot->multiexpr_params = NIL;
1154  subroot->join_domains = NIL;
1155  subroot->eq_classes = NIL;
1156  subroot->ec_merging_done = false;
1157  subroot->last_rinfo_serial = 0;
1158  subroot->all_result_relids = NULL;
1159  subroot->leaf_result_relids = NULL;
1160  subroot->append_rel_list = NIL;
1161  subroot->row_identity_vars = NIL;
1162  subroot->rowMarks = NIL;
1163  memset(subroot->upper_rels, 0, sizeof(subroot->upper_rels));
1164  memset(subroot->upper_targets, 0, sizeof(subroot->upper_targets));
1165  subroot->processed_groupClause = NIL;
1166  subroot->processed_distinctClause = NIL;
1167  subroot->processed_tlist = NIL;
1168  subroot->update_colnos = NIL;
1169  subroot->grouping_map = NULL;
1170  subroot->minmax_aggs = NIL;
1171  subroot->qual_security_level = 0;
1172  subroot->placeholdersFrozen = false;
1173  subroot->hasRecursion = false;
1174  subroot->wt_param_id = -1;
1175  subroot->non_recursive_path = NULL;
1176  /* We don't currently need a top JoinDomain for the subroot */
1177 
1178  /* No CTEs to worry about */
1179  Assert(subquery->cteList == NIL);
1180 
1181  /*
1182  * If the FROM clause is empty, replace it with a dummy RTE_RESULT RTE, so
1183  * that we don't need so many special cases to deal with that situation.
1184  */
1185  replace_empty_jointree(subquery);
1186 
1187  /*
1188  * Pull up any SubLinks within the subquery's quals, so that we don't
1189  * leave unoptimized SubLinks behind.
1190  */
1191  if (subquery->hasSubLinks)
1192  pull_up_sublinks(subroot);
1193 
1194  /*
1195  * Similarly, preprocess its function RTEs to inline any set-returning
1196  * functions in its rangetable.
1197  */
1198  preprocess_function_rtes(subroot);
1199 
1200  /*
1201  * Recursively pull up the subquery's subqueries, so that
1202  * pull_up_subqueries' processing is complete for its jointree and
1203  * rangetable.
1204  *
1205  * Note: it's okay that the subquery's recursion starts with NULL for
1206  * containing-join info, even if we are within an outer join in the upper
1207  * query; the lower query starts with a clean slate for outer-join
1208  * semantics. Likewise, we needn't pass down appendrel state.
1209  */
1210  pull_up_subqueries(subroot);
1211 
1212  /*
1213  * Now we must recheck whether the subquery is still simple enough to pull
1214  * up. If not, abandon processing it.
1215  *
1216  * We don't really need to recheck all the conditions involved, but it's
1217  * easier just to keep this "if" looking the same as the one in
1218  * pull_up_subqueries_recurse.
1219  */
1220  if (is_simple_subquery(root, subquery, rte, lowest_outer_join) &&
1221  (containing_appendrel == NULL || is_safe_append_member(subquery)))
1222  {
1223  /* good to go */
1224  }
1225  else
1226  {
1227  /*
1228  * Give up, return unmodified RangeTblRef.
1229  *
1230  * Note: The work we just did will be redone when the subquery gets
1231  * planned on its own. Perhaps we could avoid that by storing the
1232  * modified subquery back into the rangetable, but I'm not gonna risk
1233  * it now.
1234  */
1235  return jtnode;
1236  }
1237 
1238  /*
1239  * We must flatten any join alias Vars in the subquery's targetlist,
1240  * because pulling up the subquery's subqueries might have changed their
1241  * expansions into arbitrary expressions, which could affect
1242  * pullup_replace_vars' decisions about whether PlaceHolderVar wrappers
1243  * are needed for tlist entries. (Likely it'd be better to do
1244  * flatten_join_alias_vars on the whole query tree at some earlier stage,
1245  * maybe even in the rewriter; but for now let's just fix this case here.)
1246  */
1247  subquery->targetList = (List *)
1248  flatten_join_alias_vars(subroot, subroot->parse,
1249  (Node *) subquery->targetList);
1250 
1251  /*
1252  * Adjust level-0 varnos in subquery so that we can append its rangetable
1253  * to upper query's. We have to fix the subquery's append_rel_list as
1254  * well.
1255  */
1256  rtoffset = list_length(parse->rtable);
1257  OffsetVarNodes((Node *) subquery, rtoffset, 0);
1258  OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);
1259 
1260  /*
1261  * Upper-level vars in subquery are now one level closer to their parent
1262  * than before.
1263  */
1264  IncrementVarSublevelsUp((Node *) subquery, -1, 1);
1265  IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
1266 
1267  /*
1268  * The subquery's targetlist items are now in the appropriate form to
1269  * insert into the top query, except that we may need to wrap them in
1270  * PlaceHolderVars. Set up required context data for pullup_replace_vars.
1271  * (Note that we should include the subquery's inner joins in relids,
1272  * since it may include join alias vars referencing them.)
1273  */
1274  rvcontext.root = root;
1275  rvcontext.targetlist = subquery->targetList;
1276  rvcontext.target_rte = rte;
1277  if (rte->lateral)
1278  {
1279  rvcontext.relids = get_relids_in_jointree((Node *) subquery->jointree,
1280  true, true);
1281  rvcontext.nullinfo = get_nullingrels(parse);
1282  }
1283  else /* won't need these values */
1284  {
1285  rvcontext.relids = NULL;
1286  rvcontext.nullinfo = NULL;
1287  }
1288  rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
1289  rvcontext.varno = varno;
1290  /* this flag will be set below, if needed */
1291  rvcontext.wrap_non_vars = false;
1292  /* initialize cache array with indexes 0 .. length(tlist) */
1293  rvcontext.rv_cache = palloc0((list_length(subquery->targetList) + 1) *
1294  sizeof(Node *));
1295 
1296  /*
1297  * If we are dealing with an appendrel member then anything that's not a
1298  * simple Var has to be turned into a PlaceHolderVar. We force this to
1299  * ensure that what we pull up doesn't get merged into a surrounding
1300  * expression during later processing and then fail to match the
1301  * expression actually available from the appendrel.
1302  */
1303  if (containing_appendrel != NULL)
1304  rvcontext.wrap_non_vars = true;
1305 
1306  /*
1307  * If the parent query uses grouping sets, we need a PlaceHolderVar for
1308  * anything that's not a simple Var. Again, this ensures that expressions
1309  * retain their separate identity so that they will match grouping set
1310  * columns when appropriate. (It'd be sufficient to wrap values used in
1311  * grouping set columns, and do so only in non-aggregated portions of the
1312  * tlist and havingQual, but that would require a lot of infrastructure
1313  * that pullup_replace_vars hasn't currently got.)
1314  */
1315  if (parse->groupingSets)
1316  rvcontext.wrap_non_vars = true;
1317 
1318  /*
1319  * Replace all of the top query's references to the subquery's outputs
1320  * with copies of the adjusted subtlist items, being careful not to
1321  * replace any of the jointree structure.
1322  */
1323  perform_pullup_replace_vars(root, &rvcontext,
1324  containing_appendrel);
1325 
1326  /*
1327  * If the subquery had a LATERAL marker, propagate that to any of its
1328  * child RTEs that could possibly now contain lateral cross-references.
1329  * The children might or might not contain any actual lateral
1330  * cross-references, but we have to mark the pulled-up child RTEs so that
1331  * later planner stages will check for such.
1332  */
1333  if (rte->lateral)
1334  {
1335  foreach(lc, subquery->rtable)
1336  {
1337  RangeTblEntry *child_rte = (RangeTblEntry *) lfirst(lc);
1338 
1339  switch (child_rte->rtekind)
1340  {
1341  case RTE_RELATION:
1342  if (child_rte->tablesample)
1343  child_rte->lateral = true;
1344  break;
1345  case RTE_SUBQUERY:
1346  case RTE_FUNCTION:
1347  case RTE_VALUES:
1348  case RTE_TABLEFUNC:
1349  child_rte->lateral = true;
1350  break;
1351  case RTE_JOIN:
1352  case RTE_CTE:
1353  case RTE_NAMEDTUPLESTORE:
1354  case RTE_RESULT:
1355  case RTE_GROUP:
1356  /* these can't contain any lateral references */
1357  break;
1358  }
1359  }
1360  }
1361 
1362  /*
1363  * Now append the adjusted rtable entries and their perminfos to upper
1364  * query. (We hold off until after fixing the upper rtable entries; no
1365  * point in running that code on the subquery ones too.)
1366  */
1367  CombineRangeTables(&parse->rtable, &parse->rteperminfos,
1368  subquery->rtable, subquery->rteperminfos);
1369 
1370  /*
1371  * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already
1372  * adjusted the marker rtindexes, so just concat the lists.)
1373  */
1374  parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
1375 
1376  /*
1377  * We also have to fix the relid sets of any PlaceHolderVar nodes in the
1378  * parent query. (This could perhaps be done by pullup_replace_vars(),
1379  * but it seems cleaner to use two passes.) Note in particular that any
1380  * PlaceHolderVar nodes just created by pullup_replace_vars() will be
1381  * adjusted, so having created them with the subquery's varno is correct.
1382  *
1383  * Likewise, relids appearing in AppendRelInfo nodes have to be fixed. We
1384  * already checked that this won't require introducing multiple subrelids
1385  * into the single-slot AppendRelInfo structs.
1386  */
1387  if (root->glob->lastPHId != 0 || root->append_rel_list)
1388  {
1389  Relids subrelids;
1390 
1391  subrelids = get_relids_in_jointree((Node *) subquery->jointree,
1392  true, false);
1393  if (root->glob->lastPHId != 0)
1394  substitute_phv_relids((Node *) parse, varno, subrelids);
1395  fix_append_rel_relids(root, varno, subrelids);
1396  }
1397 
1398  /*
1399  * And now add subquery's AppendRelInfos to our list.
1400  */
1401  root->append_rel_list = list_concat(root->append_rel_list,
1402  subroot->append_rel_list);
1403 
1404  /*
1405  * We don't have to do the equivalent bookkeeping for outer-join info,
1406  * because that hasn't been set up yet. placeholder_list likewise.
1407  */
1408  Assert(root->join_info_list == NIL);
1409  Assert(subroot->join_info_list == NIL);
1410  Assert(root->placeholder_list == NIL);
1411  Assert(subroot->placeholder_list == NIL);
1412 
1413  /*
1414  * We no longer need the RTE's copy of the subquery's query tree. Getting
1415  * rid of it saves nothing in particular so far as this level of query is
1416  * concerned; but if this query level is in turn pulled up into a parent,
1417  * we'd waste cycles copying the now-unused query tree.
1418  */
1419  rte->subquery = NULL;
1420 
1421  /*
1422  * Miscellaneous housekeeping.
1423  *
1424  * Although replace_rte_variables() faithfully updated parse->hasSubLinks
1425  * if it copied any SubLinks out of the subquery's targetlist, we still
1426  * could have SubLinks added to the query in the expressions of FUNCTION
1427  * and VALUES RTEs copied up from the subquery. So it's necessary to copy
1428  * subquery->hasSubLinks anyway. Perhaps this can be improved someday.
1429  */
1430  parse->hasSubLinks |= subquery->hasSubLinks;
1431 
1432  /* If subquery had any RLS conditions, now main query does too */
1433  parse->hasRowSecurity |= subquery->hasRowSecurity;
1434 
1435  /*
1436  * subquery won't be pulled up if it hasAggs, hasWindowFuncs, or
1437  * hasTargetSRFs, so no work needed on those flags
1438  */
1439 
1440  /*
1441  * Return the adjusted subquery jointree to replace the RangeTblRef entry
1442  * in parent's jointree; or, if the FromExpr is degenerate, just return
1443  * its single member.
1444  */
1445  Assert(IsA(subquery->jointree, FromExpr));
1446  Assert(subquery->jointree->fromlist != NIL);
1447  if (subquery->jointree->quals == NULL &&
1448  list_length(subquery->jointree->fromlist) == 1)
1449  return (Node *) linitial(subquery->jointree->fromlist);
1450 
1451  return (Node *) subquery->jointree;
1452 }
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
@ RTE_CTE
Definition: parsenodes.h:1023
@ RTE_NAMEDTUPLESTORE
Definition: parsenodes.h:1024
@ RTE_TABLEFUNC
Definition: parsenodes.h:1021
@ RTE_RELATION
Definition: parsenodes.h:1017
static nullingrel_info * get_nullingrels(Query *parse)
void preprocess_function_rtes(PlannerInfo *root)
Definition: prepjointree.c:887
static bool is_simple_subquery(PlannerInfo *root, Query *subquery, RangeTblEntry *rte, JoinExpr *lowest_outer_join)
void pull_up_sublinks(PlannerInfo *root)
Definition: prepjointree.c:453
void replace_empty_jointree(Query *parse)
Definition: prepjointree.c:395
static void fix_append_rel_relids(PlannerInfo *root, int varno, Relids subrelids)
void pull_up_subqueries(PlannerInfo *root)
Definition: prepjointree.c:928
static bool is_safe_append_member(Query *subquery)
void OffsetVarNodes(Node *node, int offset, int sublevels_up)
Definition: rewriteManip.c:476
void CombineRangeTables(List **dst_rtable, List **dst_perminfos, List *src_rtable, List *src_perminfos)
Definition: rewriteManip.c:347
void IncrementVarSublevelsUp(Node *node, int delta_sublevels_up, int min_sublevels_up)
Definition: rewriteManip.c:841
List * minmax_aggs
Definition: pathnodes.h:478
List * processed_tlist
Definition: pathnodes.h:462
bool hasRecursion
Definition: pathnodes.h:510
List * cte_plan_ids
Definition: pathnodes.h:305
int last_rinfo_serial
Definition: pathnodes.h:343
Index qual_security_level
Definition: pathnodes.h:495
List * init_plans
Definition: pathnodes.h:299
List * multiexpr_params
Definition: pathnodes.h:308
List * row_identity_vars
Definition: pathnodes.h:368
bool ec_merging_done
Definition: pathnodes.h:317
Bitmapset * outer_params
Definition: pathnodes.h:221
Index query_level
Definition: pathnodes.h:208
List * append_rel_list
Definition: pathnodes.h:365
struct Path * non_recursive_path
Definition: pathnodes.h:538
List * placeholder_list
Definition: pathnodes.h:374
PlannerGlobal * glob
Definition: pathnodes.h:205
List * join_domains
Definition: pathnodes.h:311
List * eq_classes
Definition: pathnodes.h:314
int wt_param_id
Definition: pathnodes.h:536
List * plan_params
Definition: pathnodes.h:220
List * processed_groupClause
Definition: pathnodes.h:439
List * processed_distinctClause
Definition: pathnodes.h:451
Query * parse
Definition: pathnodes.h:202
List * rowMarks
Definition: pathnodes.h:371
List * update_colnos
Definition: pathnodes.h:470
bool placeholdersFrozen
Definition: pathnodes.h:508
List * join_info_list
Definition: pathnodes.h:340
Relids all_result_relids
Definition: pathnodes.h:354
Relids leaf_result_relids
Definition: pathnodes.h:356
struct TableSampleClause * tablesample
Definition: parsenodes.h:1098
Node * flatten_join_alias_vars(PlannerInfo *root, Query *query, Node *node)
Definition: var.c:745

References PlannerInfo::all_result_relids, PlannerInfo::append_rel_list, Assert, CombineRangeTables(), copyObject, PlannerInfo::cte_plan_ids, Query::cteList, CurrentMemoryContext, PlannerInfo::ec_merging_done, PlannerInfo::eq_classes, fix_append_rel_relids(), flatten_join_alias_vars(), FromExpr::fromlist, get_nullingrels(), get_relids_in_jointree(), PlannerInfo::glob, PlannerInfo::hasRecursion, IncrementVarSublevelsUp(), PlannerInfo::init_plans, is_safe_append_member(), is_simple_subquery(), IsA, PlannerInfo::join_domains, PlannerInfo::join_info_list, Query::jointree, PlannerInfo::last_rinfo_serial, PlannerInfo::leaf_result_relids, lfirst, linitial, list_concat(), list_length(), makeNode, PlannerInfo::minmax_aggs, PlannerInfo::multiexpr_params, NIL, PlannerInfo::non_recursive_path, pullup_replace_vars_context::nullinfo, OffsetVarNodes(), pullup_replace_vars_context::outer_hasSubLinks, PlannerInfo::outer_params, palloc0(), parse(), PlannerInfo::parse, perform_pullup_replace_vars(), PlannerInfo::placeholder_list, PlannerInfo::placeholdersFrozen, PlannerInfo::plan_params, preprocess_function_rtes(), PlannerInfo::processed_distinctClause, PlannerInfo::processed_groupClause, PlannerInfo::processed_tlist, pull_up_sublinks(), pull_up_subqueries(), PlannerInfo::qual_security_level, FromExpr::quals, PlannerInfo::query_level, pullup_replace_vars_context::relids, replace_empty_jointree(), pullup_replace_vars_context::root, root, PlannerInfo::row_identity_vars, Query::rowMarks, PlannerInfo::rowMarks, Query::rtable, RTE_CTE, RTE_FUNCTION, RTE_GROUP, RTE_JOIN, RTE_NAMEDTUPLESTORE, RTE_RELATION, RTE_RESULT, RTE_SUBQUERY, RTE_TABLEFUNC, RTE_VALUES, RangeTblEntry::rtekind, pullup_replace_vars_context::rv_cache, RangeTblEntry::subquery, substitute_phv_relids(), RangeTblEntry::tablesample, pullup_replace_vars_context::target_rte, pullup_replace_vars_context::targetlist, Query::targetList, PlannerInfo::update_colnos, pullup_replace_vars_context::varno, pullup_replace_vars_context::wrap_non_vars, and PlannerInfo::wt_param_id.

Referenced by pull_up_subqueries_recurse().

◆ pull_up_simple_union_all()

static Node * pull_up_simple_union_all ( PlannerInfo root,
Node jtnode,
RangeTblEntry rte 
)
static

Definition at line 1464 of file prepjointree.c.

1465 {
1466  int varno = ((RangeTblRef *) jtnode)->rtindex;
1467  Query *subquery = rte->subquery;
1468  int rtoffset = list_length(root->parse->rtable);
1469  List *rtable;
1470 
1471  /*
1472  * Make a modifiable copy of the subquery's rtable, so we can adjust
1473  * upper-level Vars in it. There are no such Vars in the setOperations
1474  * tree proper, so fixing the rtable should be sufficient.
1475  */
1476  rtable = copyObject(subquery->rtable);
1477 
1478  /*
1479  * Upper-level vars in subquery are now one level closer to their parent
1480  * than before. We don't have to worry about offsetting varnos, though,
1481  * because the UNION leaf queries can't cross-reference each other.
1482  */
1483  IncrementVarSublevelsUp_rtable(rtable, -1, 1);
1484 
1485  /*
1486  * If the UNION ALL subquery had a LATERAL marker, propagate that to all
1487  * its children. The individual children might or might not contain any
1488  * actual lateral cross-references, but we have to mark the pulled-up
1489  * child RTEs so that later planner stages will check for such.
1490  */
1491  if (rte->lateral)
1492  {
1493  ListCell *rt;
1494 
1495  foreach(rt, rtable)
1496  {
1497  RangeTblEntry *child_rte = (RangeTblEntry *) lfirst(rt);
1498 
1499  Assert(child_rte->rtekind == RTE_SUBQUERY);
1500  child_rte->lateral = true;
1501  }
1502  }
1503 
1504  /*
1505  * Append child RTEs (and their perminfos) to parent rtable.
1506  */
1507  CombineRangeTables(&root->parse->rtable, &root->parse->rteperminfos,
1508  rtable, subquery->rteperminfos);
1509 
1510  /*
1511  * Recursively scan the subquery's setOperations tree and add
1512  * AppendRelInfo nodes for leaf subqueries to the parent's
1513  * append_rel_list. Also apply pull_up_subqueries to the leaf subqueries.
1514  */
1515  Assert(subquery->setOperations);
1516  pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery,
1517  rtoffset);
1518 
1519  /*
1520  * Mark the parent as an append relation.
1521  */
1522  rte->inh = true;
1523 
1524  return jtnode;
1525 }
void IncrementVarSublevelsUp_rtable(List *rtable, int delta_sublevels_up, int min_sublevels_up)
Definition: rewriteManip.c:864

References Assert, CombineRangeTables(), copyObject, IncrementVarSublevelsUp_rtable(), RangeTblEntry::inh, lfirst, list_length(), pull_up_union_leaf_queries(), root, Query::rtable, RTE_SUBQUERY, RangeTblEntry::rtekind, Query::setOperations, and RangeTblEntry::subquery.

Referenced by pull_up_subqueries_recurse().

◆ pull_up_simple_values()

static Node * pull_up_simple_values ( PlannerInfo root,
Node jtnode,
RangeTblEntry rte 
)
static

Definition at line 1794 of file prepjointree.c.

1795 {
1796  Query *parse = root->parse;
1797  int varno = ((RangeTblRef *) jtnode)->rtindex;
1798  List *values_list;
1799  List *tlist;
1800  AttrNumber attrno;
1801  pullup_replace_vars_context rvcontext;
1802  ListCell *lc;
1803 
1804  Assert(rte->rtekind == RTE_VALUES);
1805  Assert(list_length(rte->values_lists) == 1);
1806 
1807  /*
1808  * Need a modifiable copy of the VALUES list to hack on, just in case it's
1809  * multiply referenced.
1810  */
1811  values_list = copyObject(linitial(rte->values_lists));
1812 
1813  /*
1814  * The VALUES RTE can't contain any Vars of level zero, let alone any that
1815  * are join aliases, so no need to flatten join alias Vars.
1816  */
1817  Assert(!contain_vars_of_level((Node *) values_list, 0));
1818 
1819  /*
1820  * Set up required context data for pullup_replace_vars. In particular,
1821  * we have to make the VALUES list look like a subquery targetlist.
1822  */
1823  tlist = NIL;
1824  attrno = 1;
1825  foreach(lc, values_list)
1826  {
1827  tlist = lappend(tlist,
1828  makeTargetEntry((Expr *) lfirst(lc),
1829  attrno,
1830  NULL,
1831  false));
1832  attrno++;
1833  }
1834  rvcontext.root = root;
1835  rvcontext.targetlist = tlist;
1836  rvcontext.target_rte = rte;
1837  rvcontext.relids = NULL; /* can't be any lateral references here */
1838  rvcontext.nullinfo = NULL;
1839  rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
1840  rvcontext.varno = varno;
1841  rvcontext.wrap_non_vars = false;
1842  /* initialize cache array with indexes 0 .. length(tlist) */
1843  rvcontext.rv_cache = palloc0((list_length(tlist) + 1) *
1844  sizeof(Node *));
1845 
1846  /*
1847  * Replace all of the top query's references to the RTE's outputs with
1848  * copies of the adjusted VALUES expressions, being careful not to replace
1849  * any of the jointree structure. We can assume there's no outer joins or
1850  * appendrels in the dummy Query that surrounds a VALUES RTE.
1851  */
1852  perform_pullup_replace_vars(root, &rvcontext, NULL);
1853 
1854  /*
1855  * There should be no appendrels to fix, nor any outer joins and hence no
1856  * PlaceHolderVars.
1857  */
1858  Assert(root->append_rel_list == NIL);
1859  Assert(root->join_info_list == NIL);
1860  Assert(root->placeholder_list == NIL);
1861 
1862  /*
1863  * Replace the VALUES RTE with a RESULT RTE. The VALUES RTE is the only
1864  * rtable entry in the current query level, so this is easy.
1865  */
1866  Assert(list_length(parse->rtable) == 1);
1867 
1868  /* Create suitable RTE */
1869  rte = makeNode(RangeTblEntry);
1870  rte->rtekind = RTE_RESULT;
1871  rte->eref = makeAlias("*RESULT*", NIL);
1872 
1873  /* Replace rangetable */
1874  parse->rtable = list_make1(rte);
1875 
1876  /* We could manufacture a new RangeTblRef, but the one we have is fine */
1877  Assert(varno == 1);
1878 
1879  return jtnode;
1880 }
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:389
bool contain_vars_of_level(Node *node, int levelsup)
Definition: var.c:443

References Assert, contain_vars_of_level(), copyObject, lappend(), lfirst, linitial, list_length(), list_make1, makeAlias(), makeNode, makeTargetEntry(), NIL, pullup_replace_vars_context::nullinfo, pullup_replace_vars_context::outer_hasSubLinks, palloc0(), parse(), perform_pullup_replace_vars(), pullup_replace_vars_context::relids, pullup_replace_vars_context::root, root, RTE_RESULT, RTE_VALUES, RangeTblEntry::rtekind, pullup_replace_vars_context::rv_cache, pullup_replace_vars_context::target_rte, pullup_replace_vars_context::targetlist, RangeTblEntry::values_lists, pullup_replace_vars_context::varno, and pullup_replace_vars_context::wrap_non_vars.

Referenced by pull_up_subqueries_recurse().

◆ pull_up_sublinks()

void pull_up_sublinks ( PlannerInfo root)

Definition at line 453 of file prepjointree.c.

454 {
455  Node *jtnode;
456  Relids relids;
457 
458  /* Begin recursion through the jointree */
460  (Node *) root->parse->jointree,
461  &relids);
462 
463  /*
464  * root->parse->jointree must always be a FromExpr, so insert a dummy one
465  * if we got a bare RangeTblRef or JoinExpr out of the recursion.
466  */
467  if (IsA(jtnode, FromExpr))
468  root->parse->jointree = (FromExpr *) jtnode;
469  else
470  root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL);
471 }
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:287
static Node * pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, Relids *relids)
Definition: prepjointree.c:480

References IsA, list_make1, makeFromExpr(), pull_up_sublinks_jointree_recurse(), and root.

Referenced by pull_up_simple_subquery(), and subquery_planner().

◆ pull_up_sublinks_jointree_recurse()

static Node * pull_up_sublinks_jointree_recurse ( PlannerInfo root,
Node jtnode,
Relids relids 
)
static

Definition at line 480 of file prepjointree.c.

482 {
483  /* Since this function recurses, it could be driven to stack overflow. */
485 
486  if (jtnode == NULL)
487  {
488  *relids = NULL;
489  }
490  else if (IsA(jtnode, RangeTblRef))
491  {
492  int varno = ((RangeTblRef *) jtnode)->rtindex;
493 
494  *relids = bms_make_singleton(varno);
495  /* jtnode is returned unmodified */
496  }
497  else if (IsA(jtnode, FromExpr))
498  {
499  FromExpr *f = (FromExpr *) jtnode;
500  List *newfromlist = NIL;
501  Relids frelids = NULL;
502  FromExpr *newf;
503  Node *jtlink;
504  ListCell *l;
505 
506  /* First, recurse to process children and collect their relids */
507  foreach(l, f->fromlist)
508  {
509  Node *newchild;
510  Relids childrelids;
511 
513  lfirst(l),
514  &childrelids);
515  newfromlist = lappend(newfromlist, newchild);
516  frelids = bms_join(frelids, childrelids);
517  }
518  /* Build the replacement FromExpr; no quals yet */
519  newf = makeFromExpr(newfromlist, NULL);
520  /* Set up a link representing the rebuilt jointree */
521  jtlink = (Node *) newf;
522  /* Now process qual --- all children are available for use */
524  &jtlink, frelids,
525  NULL, NULL);
526 
527  /*
528  * Note that the result will be either newf, or a stack of JoinExprs
529  * with newf at the base. We rely on subsequent optimization steps to
530  * flatten this and rearrange the joins as needed.
531  *
532  * Although we could include the pulled-up subqueries in the returned
533  * relids, there's no need since upper quals couldn't refer to their
534  * outputs anyway.
535  */
536  *relids = frelids;
537  jtnode = jtlink;
538  }
539  else if (IsA(jtnode, JoinExpr))
540  {
541  JoinExpr *j;
542  Relids leftrelids;
543  Relids rightrelids;
544  Node *jtlink;
545 
546  /*
547  * Make a modifiable copy of join node, but don't bother copying its
548  * subnodes (yet).
549  */
550  j = (JoinExpr *) palloc(sizeof(JoinExpr));
551  memcpy(j, jtnode, sizeof(JoinExpr));
552  jtlink = (Node *) j;
553 
554  /* Recurse to process children and collect their relids */
555  j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,
556  &leftrelids);
557  j->rarg = pull_up_sublinks_jointree_recurse(root, j->rarg,
558  &rightrelids);
559 
560  /*
561  * Now process qual, showing appropriate child relids as available,
562  * and attach any pulled-up jointree items at the right place. In the
563  * inner-join case we put new JoinExprs above the existing one (much
564  * as for a FromExpr-style join). In outer-join cases the new
565  * JoinExprs must go into the nullable side of the outer join. The
566  * point of the available_rels machinations is to ensure that we only
567  * pull up quals for which that's okay.
568  *
569  * We don't expect to see any pre-existing JOIN_SEMI, JOIN_ANTI,
570  * JOIN_RIGHT_SEMI, or JOIN_RIGHT_ANTI jointypes here.
571  */
572  switch (j->jointype)
573  {
574  case JOIN_INNER:
575  j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
576  &jtlink,
577  bms_union(leftrelids,
578  rightrelids),
579  NULL, NULL);
580  break;
581  case JOIN_LEFT:
582  j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
583  &j->rarg,
584  rightrelids,
585  NULL, NULL);
586  break;
587  case JOIN_FULL:
588  /* can't do anything with full-join quals */
589  break;
590  case JOIN_RIGHT:
591  j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
592  &j->larg,
593  leftrelids,
594  NULL, NULL);
595  break;
596  default:
597  elog(ERROR, "unrecognized join type: %d",
598  (int) j->jointype);
599  break;
600  }
601 
602  /*
603  * Although we could include the pulled-up subqueries in the returned
604  * relids, there's no need since upper quals couldn't refer to their
605  * outputs anyway. But we *do* need to include the join's own rtindex
606  * because we haven't yet collapsed join alias variables, so upper
607  * levels would mistakenly think they couldn't use references to this
608  * join.
609  */
610  *relids = bms_join(leftrelids, rightrelids);
611  if (j->rtindex)
612  *relids = bms_add_member(*relids, j->rtindex);
613  jtnode = jtlink;
614  }
615  else
616  elog(ERROR, "unrecognized node type: %d",
617  (int) nodeTag(jtnode));
618  return jtnode;
619 }
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:251
void * palloc(Size size)
Definition: mcxt.c:1317
static Node * pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, Node **jtlink1, Relids available_rels1, Node **jtlink2, Relids available_rels2)
Definition: prepjointree.c:637

References bms_add_member(), bms_join(), bms_make_singleton(), bms_union(), check_stack_depth(), elog, ERROR, FromExpr::fromlist, IsA, j, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, lappend(), lfirst, makeFromExpr(), NIL, nodeTag, palloc(), pull_up_sublinks_qual_recurse(), FromExpr::quals, and root.

Referenced by pull_up_sublinks(), and pull_up_sublinks_qual_recurse().

◆ pull_up_sublinks_qual_recurse()

static Node * pull_up_sublinks_qual_recurse ( PlannerInfo root,
Node node,
Node **  jtlink1,
Relids  available_rels1,
Node **  jtlink2,
Relids  available_rels2 
)
static

Definition at line 637 of file prepjointree.c.

640 {
641  if (node == NULL)
642  return NULL;
643  if (IsA(node, SubLink))
644  {
645  SubLink *sublink = (SubLink *) node;
646  JoinExpr *j;
647  Relids child_rels;
648 
649  /* Is it a convertible ANY or EXISTS clause? */
650  if (sublink->subLinkType == ANY_SUBLINK)
651  {
652  if ((j = convert_ANY_sublink_to_join(root, sublink,
653  available_rels1)) != NULL)
654  {
655  /* Yes; insert the new join node into the join tree */
656  j->larg = *jtlink1;
657  *jtlink1 = (Node *) j;
658  /* Recursively process pulled-up jointree nodes */
660  j->rarg,
661  &child_rels);
662 
663  /*
664  * Now recursively process the pulled-up quals. Any inserted
665  * joins can get stacked onto either j->larg or j->rarg,
666  * depending on which rels they reference.
667  */
669  j->quals,
670  &j->larg,
671  available_rels1,
672  &j->rarg,
673  child_rels);
674  /* Return NULL representing constant TRUE */
675  return NULL;
676  }
677  if (available_rels2 != NULL &&
678  (j = convert_ANY_sublink_to_join(root, sublink,
679  available_rels2)) != NULL)
680  {
681  /* Yes; insert the new join node into the join tree */
682  j->larg = *jtlink2;
683  *jtlink2 = (Node *) j;
684  /* Recursively process pulled-up jointree nodes */
686  j->rarg,
687  &child_rels);
688 
689  /*
690  * Now recursively process the pulled-up quals. Any inserted
691  * joins can get stacked onto either j->larg or j->rarg,
692  * depending on which rels they reference.
693  */
695  j->quals,
696  &j->larg,
697  available_rels2,
698  &j->rarg,
699  child_rels);
700  /* Return NULL representing constant TRUE */
701  return NULL;
702  }
703  }
704  else if (sublink->subLinkType == EXISTS_SUBLINK)
705  {
706  if ((j = convert_EXISTS_sublink_to_join(root, sublink, false,
707  available_rels1)) != NULL)
708  {
709  /* Yes; insert the new join node into the join tree */
710  j->larg = *jtlink1;
711  *jtlink1 = (Node *) j;
712  /* Recursively process pulled-up jointree nodes */
714  j->rarg,
715  &child_rels);
716 
717  /*
718  * Now recursively process the pulled-up quals. Any inserted
719  * joins can get stacked onto either j->larg or j->rarg,
720  * depending on which rels they reference.
721  */
723  j->quals,
724  &j->larg,
725  available_rels1,
726  &j->rarg,
727  child_rels);
728  /* Return NULL representing constant TRUE */
729  return NULL;
730  }
731  if (available_rels2 != NULL &&
732  (j = convert_EXISTS_sublink_to_join(root, sublink, false,
733  available_rels2)) != NULL)
734  {
735  /* Yes; insert the new join node into the join tree */
736  j->larg = *jtlink2;
737  *jtlink2 = (Node *) j;
738  /* Recursively process pulled-up jointree nodes */
740  j->rarg,
741  &child_rels);
742 
743  /*
744  * Now recursively process the pulled-up quals. Any inserted
745  * joins can get stacked onto either j->larg or j->rarg,
746  * depending on which rels they reference.
747  */
749  j->quals,
750  &j->larg,
751  available_rels2,
752  &j->rarg,
753  child_rels);
754  /* Return NULL representing constant TRUE */
755  return NULL;
756  }
757  }
758  /* Else return it unmodified */
759  return node;
760  }
761  if (is_notclause(node))
762  {
763  /* If the immediate argument of NOT is EXISTS, try to convert */
764  SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node);
765  JoinExpr *j;
766  Relids child_rels;
767 
768  if (sublink && IsA(sublink, SubLink))
769  {
770  if (sublink->subLinkType == EXISTS_SUBLINK)
771  {
772  if ((j = convert_EXISTS_sublink_to_join(root, sublink, true,
773  available_rels1)) != NULL)
774  {
775  /* Yes; insert the new join node into the join tree */
776  j->larg = *jtlink1;
777  *jtlink1 = (Node *) j;
778  /* Recursively process pulled-up jointree nodes */
780  j->rarg,
781  &child_rels);
782 
783  /*
784  * Now recursively process the pulled-up quals. Because
785  * we are underneath a NOT, we can't pull up sublinks that
786  * reference the left-hand stuff, but it's still okay to
787  * pull up sublinks referencing j->rarg.
788  */
790  j->quals,
791  &j->rarg,
792  child_rels,
793  NULL, NULL);
794  /* Return NULL representing constant TRUE */
795  return NULL;
796  }
797  if (available_rels2 != NULL &&
798  (j = convert_EXISTS_sublink_to_join(root, sublink, true,
799  available_rels2)) != NULL)
800  {
801  /* Yes; insert the new join node into the join tree */
802  j->larg = *jtlink2;
803  *jtlink2 = (Node *) j;
804  /* Recursively process pulled-up jointree nodes */
806  j->rarg,
807  &child_rels);
808 
809  /*
810  * Now recursively process the pulled-up quals. Because
811  * we are underneath a NOT, we can't pull up sublinks that
812  * reference the left-hand stuff, but it's still okay to
813  * pull up sublinks referencing j->rarg.
814  */
816  j->quals,
817  &j->rarg,
818  child_rels,
819  NULL, NULL);
820  /* Return NULL representing constant TRUE */
821  return NULL;
822  }
823  }
824  }
825  /* Else return it unmodified */
826  return node;
827  }
828  if (is_andclause(node))
829  {
830  /* Recurse into AND clause */
831  List *newclauses = NIL;
832  ListCell *l;
833 
834  foreach(l, ((BoolExpr *) node)->args)
835  {
836  Node *oldclause = (Node *) lfirst(l);
837  Node *newclause;
838 
840  oldclause,
841  jtlink1,
842  available_rels1,
843  jtlink2,
844  available_rels2);
845  if (newclause)
846  newclauses = lappend(newclauses, newclause);
847  }
848  /* We might have got back fewer clauses than we started with */
849  if (newclauses == NIL)
850  return NULL;
851  else if (list_length(newclauses) == 1)
852  return (Node *) linitial(newclauses);
853  else
854  return (Node *) make_andclause(newclauses);
855  }
856  /* Stop if not an AND */
857  return node;
858 }
Expr * make_andclause(List *andclauses)
Definition: makefuncs.c:677
static bool is_andclause(const void *clause)
Definition: nodeFuncs.h:107
static Expr * get_notclausearg(const void *notclause)
Definition: nodeFuncs.h:134
static bool is_notclause(const void *clause)
Definition: nodeFuncs.h:125
@ ANY_SUBLINK
Definition: primnodes.h:999
@ EXISTS_SUBLINK
Definition: primnodes.h:997
JoinExpr * convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, Relids available_rels)
Definition: subselect.c:1251
JoinExpr * convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, bool under_not, Relids available_rels)
Definition: subselect.c:1368

References ANY_SUBLINK, generate_unaccent_rules::args, convert_ANY_sublink_to_join(), convert_EXISTS_sublink_to_join(), EXISTS_SUBLINK, get_notclausearg(), is_andclause(), is_notclause(), IsA, j, lappend(), lfirst, linitial, list_length(), make_andclause(), NIL, pull_up_sublinks_jointree_recurse(), root, and SubLink::subLinkType.

Referenced by pull_up_sublinks_jointree_recurse().

◆ pull_up_subqueries()

void pull_up_subqueries ( PlannerInfo root)

Definition at line 928 of file prepjointree.c.

929 {
930  /* Top level of jointree must always be a FromExpr */
931  Assert(IsA(root->parse->jointree, FromExpr));
932  /* Recursion starts with no containing join nor appendrel */
933  root->parse->jointree = (FromExpr *)
934  pull_up_subqueries_recurse(root, (Node *) root->parse->jointree,
935  NULL, NULL);
936  /* We should still have a FromExpr */
937  Assert(IsA(root->parse->jointree, FromExpr));
938 }
static Node * pull_up_subqueries_recurse(PlannerInfo *root, Node *jtnode, JoinExpr *lowest_outer_join, AppendRelInfo *containing_appendrel)
Definition: prepjointree.c:972

References Assert, IsA, pull_up_subqueries_recurse(), and root.

Referenced by pull_up_simple_subquery(), and subquery_planner().

◆ pull_up_subqueries_recurse()

static Node * pull_up_subqueries_recurse ( PlannerInfo root,
Node jtnode,
JoinExpr lowest_outer_join,
AppendRelInfo containing_appendrel 
)
static

Definition at line 972 of file prepjointree.c.

975 {
976  /* Since this function recurses, it could be driven to stack overflow. */
978  /* Also, since it's a bit expensive, let's check for query cancel. */
980 
981  Assert(jtnode != NULL);
982  if (IsA(jtnode, RangeTblRef))
983  {
984  int varno = ((RangeTblRef *) jtnode)->rtindex;
985  RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable);
986 
987  /*
988  * Is this a subquery RTE, and if so, is the subquery simple enough to
989  * pull up?
990  *
991  * If we are looking at an append-relation member, we can't pull it up
992  * unless is_safe_append_member says so.
993  */
994  if (rte->rtekind == RTE_SUBQUERY &&
995  is_simple_subquery(root, rte->subquery, rte, lowest_outer_join) &&
996  (containing_appendrel == NULL ||
998  return pull_up_simple_subquery(root, jtnode, rte,
999  lowest_outer_join,
1000  containing_appendrel);
1001 
1002  /*
1003  * Alternatively, is it a simple UNION ALL subquery? If so, flatten
1004  * into an "append relation".
1005  *
1006  * It's safe to do this regardless of whether this query is itself an
1007  * appendrel member. (If you're thinking we should try to flatten the
1008  * two levels of appendrel together, you're right; but we handle that
1009  * in set_append_rel_pathlist, not here.)
1010  */
1011  if (rte->rtekind == RTE_SUBQUERY &&
1013  return pull_up_simple_union_all(root, jtnode, rte);
1014 
1015  /*
1016  * Or perhaps it's a simple VALUES RTE?
1017  *
1018  * We don't allow VALUES pullup below an outer join nor into an
1019  * appendrel (such cases are impossible anyway at the moment).
1020  */
1021  if (rte->rtekind == RTE_VALUES &&
1022  lowest_outer_join == NULL &&
1023  containing_appendrel == NULL &&
1024  is_simple_values(root, rte))
1025  return pull_up_simple_values(root, jtnode, rte);
1026 
1027  /*
1028  * Or perhaps it's a FUNCTION RTE that we could inline?
1029  */
1030  if (rte->rtekind == RTE_FUNCTION)
1031  return pull_up_constant_function(root, jtnode, rte,
1032  containing_appendrel);
1033 
1034  /* Otherwise, do nothing at this node. */
1035  }
1036  else if (IsA(jtnode, FromExpr))
1037  {
1038  FromExpr *f = (FromExpr *) jtnode;
1039  ListCell *l;
1040 
1041  Assert(containing_appendrel == NULL);
1042  /* Recursively transform all the child nodes */
1043  foreach(l, f->fromlist)
1044  {
1046  lowest_outer_join,
1047  NULL);
1048  }
1049  }
1050  else if (IsA(jtnode, JoinExpr))
1051  {
1052  JoinExpr *j = (JoinExpr *) jtnode;
1053 
1054  Assert(containing_appendrel == NULL);
1055  /* Recurse, being careful to tell myself when inside outer join */
1056  switch (j->jointype)
1057  {
1058  case JOIN_INNER:
1059  j->larg = pull_up_subqueries_recurse(root, j->larg,
1060  lowest_outer_join,
1061  NULL);
1062  j->rarg = pull_up_subqueries_recurse(root, j->rarg,
1063  lowest_outer_join,
1064  NULL);
1065  break;
1066  case JOIN_LEFT:
1067  case JOIN_SEMI:
1068  case JOIN_ANTI:
1069  j->larg = pull_up_subqueries_recurse(root, j->larg,
1070  j,
1071  NULL);
1072  j->rarg = pull_up_subqueries_recurse(root, j->rarg,
1073  j,
1074  NULL);
1075  break;
1076  case JOIN_FULL:
1077  j->larg = pull_up_subqueries_recurse(root, j->larg,
1078  j,
1079  NULL);
1080  j->rarg = pull_up_subqueries_recurse(root, j->rarg,
1081  j,
1082  NULL);
1083  break;
1084  case JOIN_RIGHT:
1085  j->larg = pull_up_subqueries_recurse(root, j->larg,
1086  j,
1087  NULL);
1088  j->rarg = pull_up_subqueries_recurse(root, j->rarg,
1089  j,
1090  NULL);
1091  break;
1092  default:
1093  elog(ERROR, "unrecognized join type: %d",
1094  (int) j->jointype);
1095  break;
1096  }
1097  }
1098  else
1099  elog(ERROR, "unrecognized node type: %d",
1100  (int) nodeTag(jtnode));
1101  return jtnode;
1102 }
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
static Node * pull_up_constant_function(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, AppendRelInfo *containing_appendrel)
static Node * pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
static bool is_simple_values(PlannerInfo *root, RangeTblEntry *rte)
static Node * pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, JoinExpr *lowest_outer_join, AppendRelInfo *containing_appendrel)
static Node * pull_up_simple_values(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
static bool is_simple_union_all(Query *subquery)

References Assert, CHECK_FOR_INTERRUPTS, check_stack_depth(), elog, ERROR, FromExpr::fromlist, is_safe_append_member(), is_simple_subquery(), is_simple_union_all(), is_simple_values(), IsA, j, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, lfirst, nodeTag, pull_up_constant_function(), pull_up_simple_subquery(), pull_up_simple_union_all(), pull_up_simple_values(), root, rt_fetch, RTE_FUNCTION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, and RangeTblEntry::subquery.

Referenced by pull_up_subqueries(), and pull_up_union_leaf_queries().

◆ pull_up_union_leaf_queries()

static void pull_up_union_leaf_queries ( Node setOp,
PlannerInfo root,
int  parentRTindex,
Query setOpQuery,
int  childRToffset 
)
static

Definition at line 1546 of file prepjointree.c.

1548 {
1549  if (IsA(setOp, RangeTblRef))
1550  {
1551  RangeTblRef *rtr = (RangeTblRef *) setOp;
1552  int childRTindex;
1553  AppendRelInfo *appinfo;
1554 
1555  /*
1556  * Calculate the index in the parent's range table
1557  */
1558  childRTindex = childRToffset + rtr->rtindex;
1559 
1560  /*
1561  * Build a suitable AppendRelInfo, and attach to parent's list.
1562  */
1563  appinfo = makeNode(AppendRelInfo);
1564  appinfo->parent_relid = parentRTindex;
1565  appinfo->child_relid = childRTindex;
1566  appinfo->parent_reltype = InvalidOid;
1567  appinfo->child_reltype = InvalidOid;
1568  make_setop_translation_list(setOpQuery, childRTindex, appinfo);
1569  appinfo->parent_reloid = InvalidOid;
1570  root->append_rel_list = lappend(root->append_rel_list, appinfo);
1571 
1572  /*
1573  * Recursively apply pull_up_subqueries to the new child RTE. (We
1574  * must build the AppendRelInfo first, because this will modify it;
1575  * indeed, that's the only part of the upper query where Vars
1576  * referencing childRTindex can exist at this point.)
1577  *
1578  * Note that we can pass NULL for containing-join info even if we're
1579  * actually under an outer join, because the child's expressions
1580  * aren't going to propagate up to the join. Also, we ignore the
1581  * possibility that pull_up_subqueries_recurse() returns a different
1582  * jointree node than what we pass it; if it does, the important thing
1583  * is that it replaced the child relid in the AppendRelInfo node.
1584  */
1585  rtr = makeNode(RangeTblRef);
1586  rtr->rtindex = childRTindex;
1587  (void) pull_up_subqueries_recurse(root, (Node *) rtr,
1588  NULL, appinfo);
1589  }
1590  else if (IsA(setOp, SetOperationStmt))
1591  {
1592  SetOperationStmt *op = (SetOperationStmt *) setOp;
1593 
1594  /* Recurse to reach leaf queries */
1595  pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery,
1596  childRToffset);
1597  pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery,
1598  childRToffset);
1599  }
1600  else
1601  {
1602  elog(ERROR, "unrecognized node type: %d",
1603  (int) nodeTag(setOp));
1604  }
1605 }
#define InvalidOid
Definition: postgres_ext.h:36
static void make_setop_translation_list(Query *query, int newvarno, AppendRelInfo *appinfo)
Oid parent_reltype
Definition: pathnodes.h:2988

References AppendRelInfo::child_relid, AppendRelInfo::child_reltype, elog, ERROR, InvalidOid, IsA, lappend(), SetOperationStmt::larg, make_setop_translation_list(), makeNode, nodeTag, AppendRelInfo::parent_relid, AppendRelInfo::parent_reloid, AppendRelInfo::parent_reltype, pull_up_subqueries_recurse(), SetOperationStmt::rarg, root, and RangeTblRef::rtindex.

Referenced by flatten_simple_union_all(), and pull_up_simple_union_all().

◆ pullup_replace_vars()

static Node * pullup_replace_vars ( Node expr,
pullup_replace_vars_context context 
)
static

Definition at line 2475 of file prepjointree.c.

2476 {
2477  return replace_rte_variables(expr,
2478  context->varno, 0,
2480  context,
2481  context->outer_hasSubLinks);
2482 }
static Node * pullup_replace_vars_callback(Var *var, replace_rte_variables_context *context)
Node * replace_rte_variables(Node *node, int target_varno, int sublevels_up, replace_rte_variables_callback callback, void *callback_arg, bool *outer_hasSubLinks)

References context, pullup_replace_vars_callback(), and replace_rte_variables().

Referenced by perform_pullup_replace_vars(), and replace_vars_in_jointree().

◆ pullup_replace_vars_callback()

static Node * pullup_replace_vars_callback ( Var var,
replace_rte_variables_context context 
)
static

Definition at line 2485 of file prepjointree.c.

2487 {
2489  int varattno = var->varattno;
2490  bool need_phv;
2491  Node *newnode;
2492 
2493  /*
2494  * We need a PlaceHolderVar if the Var-to-be-replaced has nonempty
2495  * varnullingrels (unless we find below that the replacement expression is
2496  * a Var or PlaceHolderVar that we can just add the nullingrels to). We
2497  * also need one if the caller has instructed us that all non-Var/PHV
2498  * replacements need to be wrapped for identification purposes.
2499  */
2500  need_phv = (var->varnullingrels != NULL) || rcon->wrap_non_vars;
2501 
2502  /*
2503  * If PlaceHolderVars are needed, we cache the modified expressions in
2504  * rcon->rv_cache[]. This is not in hopes of any material speed gain
2505  * within this function, but to avoid generating identical PHVs with
2506  * different IDs. That would result in duplicate evaluations at runtime,
2507  * and possibly prevent optimizations that rely on recognizing different
2508  * references to the same subquery output as being equal(). So it's worth
2509  * a bit of extra effort to avoid it.
2510  *
2511  * The cached items have phlevelsup = 0 and phnullingrels = NULL; we'll
2512  * copy them and adjust those values for this reference site below.
2513  */
2514  if (need_phv &&
2515  varattno >= InvalidAttrNumber &&
2516  varattno <= list_length(rcon->targetlist) &&
2517  rcon->rv_cache[varattno] != NULL)
2518  {
2519  /* Just copy the entry and fall through to adjust phlevelsup etc */
2520  newnode = copyObject(rcon->rv_cache[varattno]);
2521  }
2522  else if (varattno == InvalidAttrNumber)
2523  {
2524  /* Must expand whole-tuple reference into RowExpr */
2525  RowExpr *rowexpr;
2526  List *colnames;
2527  List *fields;
2528  bool save_wrap_non_vars = rcon->wrap_non_vars;
2529  int save_sublevelsup = context->sublevels_up;
2530 
2531  /*
2532  * If generating an expansion for a var of a named rowtype (ie, this
2533  * is a plain relation RTE), then we must include dummy items for
2534  * dropped columns. If the var is RECORD (ie, this is a JOIN), then
2535  * omit dropped columns. In the latter case, attach column names to
2536  * the RowExpr for use of the executor and ruleutils.c.
2537  *
2538  * In order to be able to cache the results, we always generate the
2539  * expansion with varlevelsup = 0, and then adjust below if needed.
2540  */
2541  expandRTE(rcon->target_rte,
2542  var->varno, 0 /* not varlevelsup */ , var->location,
2543  (var->vartype != RECORDOID),
2544  &colnames, &fields);
2545  /* Expand the generated per-field Vars, but don't insert PHVs there */
2546  rcon->wrap_non_vars = false;
2547  context->sublevels_up = 0; /* to match the expandRTE output */
2548  fields = (List *) replace_rte_variables_mutator((Node *) fields,
2549  context);
2550  rcon->wrap_non_vars = save_wrap_non_vars;
2551  context->sublevels_up = save_sublevelsup;
2552 
2553  rowexpr = makeNode(RowExpr);
2554  rowexpr->args = fields;
2555  rowexpr->row_typeid = var->vartype;
2556  rowexpr->row_format = COERCE_IMPLICIT_CAST;
2557  rowexpr->colnames = (var->vartype == RECORDOID) ? colnames : NIL;
2558  rowexpr->location = var->location;
2559  newnode = (Node *) rowexpr;
2560 
2561  /*
2562  * Insert PlaceHolderVar if needed. Notice that we are wrapping one
2563  * PlaceHolderVar around the whole RowExpr, rather than putting one
2564  * around each element of the row. This is because we need the
2565  * expression to yield NULL, not ROW(NULL,NULL,...) when it is forced
2566  * to null by an outer join.
2567  */
2568  if (need_phv)
2569  {
2570  newnode = (Node *)
2572  (Expr *) newnode,
2573  bms_make_singleton(rcon->varno));
2574  /* cache it with the PHV, and with phlevelsup etc not set yet */
2575  rcon->rv_cache[InvalidAttrNumber] = copyObject(newnode);
2576  }
2577  }
2578  else
2579  {
2580  /* Normal case referencing one targetlist element */
2581  TargetEntry *tle = get_tle_by_resno(rcon->targetlist, varattno);
2582 
2583  if (tle == NULL) /* shouldn't happen */
2584  elog(ERROR, "could not find attribute %d in subquery targetlist",
2585  varattno);
2586 
2587  /* Make a copy of the tlist item to return */
2588  newnode = (Node *) copyObject(tle->expr);
2589 
2590  /* Insert PlaceHolderVar if needed */
2591  if (need_phv)
2592  {
2593  bool wrap;
2594 
2595  if (newnode && IsA(newnode, Var) &&
2596  ((Var *) newnode)->varlevelsup == 0)
2597  {
2598  /*
2599  * Simple Vars always escape being wrapped, unless they are
2600  * lateral references to something outside the subquery being
2601  * pulled up. (Even then, we could omit the PlaceHolderVar if
2602  * the referenced rel is under the same lowest outer join, but
2603  * it doesn't seem worth the trouble to check that.)
2604  */
2605  if (rcon->target_rte->lateral &&
2606  !bms_is_member(((Var *) newnode)->varno, rcon->relids))
2607  wrap = true;
2608  else
2609  wrap = false;
2610  }
2611  else if (newnode && IsA(newnode, PlaceHolderVar) &&
2612  ((PlaceHolderVar *) newnode)->phlevelsup == 0)
2613  {
2614  /* The same rules apply for a PlaceHolderVar */
2615  if (rcon->target_rte->lateral &&
2616  !bms_is_subset(((PlaceHolderVar *) newnode)->phrels,
2617  rcon->relids))
2618  wrap = true;
2619  else
2620  wrap = false;
2621  }
2622  else if (rcon->wrap_non_vars)
2623  {
2624  /* Caller told us to wrap all non-Vars in a PlaceHolderVar */
2625  wrap = true;
2626  }
2627  else
2628  {
2629  /*
2630  * If the node contains Var(s) or PlaceHolderVar(s) of the
2631  * subquery being pulled up, and does not contain any
2632  * non-strict constructs, then instead of adding a PHV on top
2633  * we can add the required nullingrels to those Vars/PHVs.
2634  * (This is fundamentally a generalization of the above cases
2635  * for bare Vars and PHVs.)
2636  *
2637  * This test is somewhat expensive, but it avoids pessimizing
2638  * the plan in cases where the nullingrels get removed again
2639  * later by outer join reduction.
2640  *
2641  * Note that we don't force wrapping of expressions containing
2642  * lateral references, so long as they also contain Vars/PHVs
2643  * of the subquery. This is okay because of the restriction
2644  * to strict constructs: if the subquery's Vars/PHVs have been
2645  * forced to NULL by an outer join then the end result of the
2646  * expression will be NULL too, regardless of the lateral
2647  * references. So it's not necessary to force the expression
2648  * to be evaluated below the outer join. This can be a very
2649  * valuable optimization, because it may allow us to avoid
2650  * using a nested loop to pass the lateral reference down.
2651  *
2652  * This analysis could be tighter: in particular, a non-strict
2653  * construct hidden within a lower-level PlaceHolderVar is not
2654  * reason to add another PHV. But for now it doesn't seem
2655  * worth the code to be more exact.
2656  *
2657  * For a LATERAL subquery, we have to check the actual var
2658  * membership of the node, but if it's non-lateral then any
2659  * level-zero var must belong to the subquery.
2660  */
2661  if ((rcon->target_rte->lateral ?
2662  bms_overlap(pull_varnos(rcon->root, newnode),
2663  rcon->relids) :
2664  contain_vars_of_level(newnode, 0)) &&
2665  !contain_nonstrict_functions(newnode))
2666  {
2667  /* No wrap needed */
2668  wrap = false;
2669  }
2670  else
2671  {
2672  /* Else wrap it in a PlaceHolderVar */
2673  wrap = true;
2674  }
2675  }
2676 
2677  if (wrap)
2678  {
2679  newnode = (Node *)
2681  (Expr *) newnode,
2682  bms_make_singleton(rcon->varno));
2683 
2684  /*
2685  * Cache it if possible (ie, if the attno is in range, which
2686  * it probably always should be).
2687  */
2688  if (varattno > InvalidAttrNumber &&
2689  varattno <= list_length(rcon->targetlist))
2690  rcon->rv_cache[varattno] = copyObject(newnode);
2691  }
2692  }
2693  }
2694 
2695  /* Propagate any varnullingrels into the replacement expression */
2696  if (var->varnullingrels != NULL)
2697  {
2698  if (IsA(newnode, Var))
2699  {
2700  Var *newvar = (Var *) newnode;
2701 
2702  Assert(newvar->varlevelsup == 0);
2703  newvar->varnullingrels = bms_add_members(newvar->varnullingrels,
2704  var->varnullingrels);
2705  }
2706  else if (IsA(newnode, PlaceHolderVar))
2707  {
2708  PlaceHolderVar *newphv = (PlaceHolderVar *) newnode;
2709 
2710  Assert(newphv->phlevelsup == 0);
2711  newphv->phnullingrels = bms_add_members(newphv->phnullingrels,
2712  var->varnullingrels);
2713  }
2714  else
2715  {
2716  /*
2717  * There should be Vars/PHVs within the expression that we can
2718  * modify. Vars/PHVs of the subquery should have the full
2719  * var->varnullingrels added to them, but if there are lateral
2720  * references within the expression, those must be marked with
2721  * only the nullingrels that potentially apply to them. (This
2722  * corresponds to the fact that the expression will now be
2723  * evaluated at the join level of the Var that we are replacing:
2724  * the lateral references may have bubbled up through fewer outer
2725  * joins than the subquery's Vars have. Per the discussion above,
2726  * we'll still get the right answers.) That relid set could be
2727  * different for different lateral relations, so we have to do
2728  * this work for each one.
2729  *
2730  * (Currently, the restrictions in is_simple_subquery() mean that
2731  * at most we have to remove the lowest outer join's relid from
2732  * the nullingrels of a lateral reference. However, we might
2733  * relax those restrictions someday, so let's do this right.)
2734  */
2735  if (rcon->target_rte->lateral)
2736  {
2737  nullingrel_info *nullinfo = rcon->nullinfo;
2738  Relids lvarnos;
2739  int lvarno;
2740 
2741  /*
2742  * Identify lateral varnos used within newnode. We must do
2743  * this before injecting var->varnullingrels into the tree.
2744  */
2745  lvarnos = pull_varnos(rcon->root, newnode);
2746  lvarnos = bms_del_members(lvarnos, rcon->relids);
2747  /* For each one, add relevant nullingrels if any */
2748  lvarno = -1;
2749  while ((lvarno = bms_next_member(lvarnos, lvarno)) >= 0)
2750  {
2751  Relids lnullingrels;
2752 
2753  Assert(lvarno > 0 && lvarno <= nullinfo->rtlength);
2754  lnullingrels = bms_intersect(var->varnullingrels,
2755  nullinfo->nullingrels[lvarno]);
2756  if (!bms_is_empty(lnullingrels))
2757  newnode = add_nulling_relids(newnode,
2758  bms_make_singleton(lvarno),
2759  lnullingrels);
2760  }
2761  }
2762 
2763  /* Finally, deal with Vars/PHVs of the subquery itself */
2764  newnode = add_nulling_relids(newnode,
2765  rcon->relids,
2766  var->varnullingrels);
2767  /* Assert we did put the varnullingrels into the expression */
2768  Assert(bms_is_subset(var->varnullingrels,
2769  pull_varnos(rcon->root, newnode)));
2770  }
2771  }
2772 
2773  /* Must adjust varlevelsup if replaced Var is within a subquery */
2774  if (var->varlevelsup > 0)
2775  IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
2776 
2777  return newnode;
2778 }
#define InvalidAttrNumber
Definition: attnum.h:23
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
Bitmapset * bms_intersect(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:292
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:917
Bitmapset * bms_del_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:1161
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:582
#define bms_is_empty(a)
Definition: bitmapset.h:118
bool contain_nonstrict_functions(Node *clause)
Definition: clauses.c:992
TargetEntry * get_tle_by_resno(List *tlist, AttrNumber resno)
void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars)
PlaceHolderVar * make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
Definition: placeholder.c:54
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:736
Node * replace_rte_variables_mutator(Node *node, replace_rte_variables_context *context)
Node * add_nulling_relids(Node *node, const Bitmapset *target_relids, const Bitmapset *added_relids)
Relids phnullingrels
Definition: pathnodes.h:2801
List * args
Definition: primnodes.h:1411
ParseLoc location
Definition: primnodes.h:1435
Expr * expr
Definition: primnodes.h:2190
Definition: primnodes.h:248
ParseLoc location
Definition: primnodes.h:293
AttrNumber varattno
Definition: primnodes.h:260
int varno
Definition: primnodes.h:255
Index varlevelsup
Definition: primnodes.h:280
Relids pull_varnos(PlannerInfo *root, Node *node)
Definition: var.c:113

References add_nulling_relids(), RowExpr::args, Assert, bms_add_members(), bms_del_members(), bms_intersect(), bms_is_empty, bms_is_member(), bms_is_subset(), bms_make_singleton(), bms_next_member(), bms_overlap(), COERCE_IMPLICIT_CAST, contain_nonstrict_functions(), contain_vars_of_level(), context, copyObject, elog, ERROR, expandRTE(), TargetEntry::expr, get_tle_by_resno(), IncrementVarSublevelsUp(), InvalidAttrNumber, IsA, list_length(), Var::location, RowExpr::location, make_placeholder_expr(), makeNode, NIL, pullup_replace_vars_context::nullinfo, nullingrel_info::nullingrels, PlaceHolderVar::phlevelsup, PlaceHolderVar::phnullingrels, pull_varnos(), pullup_replace_vars_context::relids, replace_rte_variables_mutator(), pullup_replace_vars_context::root, pullup_replace_vars_context::rv_cache, pullup_replace_vars_context::target_rte, pullup_replace_vars_context::targetlist, Var::varattno, Var::varlevelsup, pullup_replace_vars_context::varno, Var::varno, and pullup_replace_vars_context::wrap_non_vars.

Referenced by pullup_replace_vars(), and pullup_replace_vars_subquery().

◆ pullup_replace_vars_subquery()

static Query * pullup_replace_vars_subquery ( Query query,
pullup_replace_vars_context context 
)
static

Definition at line 2788 of file prepjointree.c.

2790 {
2791  Assert(IsA(query, Query));
2792  return (Query *) replace_rte_variables((Node *) query,
2793  context->varno, 1,
2795  context,
2796  NULL);
2797 }

References Assert, context, IsA, pullup_replace_vars_callback(), and replace_rte_variables().

Referenced by replace_vars_in_jointree().

◆ reduce_outer_joins()

void reduce_outer_joins ( PlannerInfo root)

Definition at line 2934 of file prepjointree.c.

2935 {
2938  ListCell *lc;
2939 
2940  /*
2941  * To avoid doing strictness checks on more quals than necessary, we want
2942  * to stop descending the jointree as soon as there are no outer joins
2943  * below our current point. This consideration forces a two-pass process.
2944  * The first pass gathers information about which base rels appear below
2945  * each side of each join clause, and about whether there are outer
2946  * join(s) below each side of each join clause. The second pass examines
2947  * qual clauses and changes join types as it descends the tree.
2948  */
2949  state1 = reduce_outer_joins_pass1((Node *) root->parse->jointree);
2950 
2951  /* planner.c shouldn't have called me if no outer joins */
2952  if (state1 == NULL || !state1->contains_outer)
2953  elog(ERROR, "so where are the outer joins?");
2954 
2955  state2.inner_reduced = NULL;
2956  state2.partial_reduced = NIL;
2957 
2958  reduce_outer_joins_pass2((Node *) root->parse->jointree,
2959  state1, &state2,
2960  root, NULL, NIL);
2961 
2962  /*
2963  * If we successfully reduced the strength of any outer joins, we must
2964  * remove references to those joins as nulling rels. This is handled as
2965  * an additional pass, for simplicity and because we can handle all
2966  * fully-reduced joins in a single pass over the parse tree.
2967  */
2968  if (!bms_is_empty(state2.inner_reduced))
2969  {
2970  root->parse = (Query *)
2971  remove_nulling_relids((Node *) root->parse,
2972  state2.inner_reduced,
2973  NULL);
2974  /* There could be references in the append_rel_list, too */
2975  root->append_rel_list = (List *)
2976  remove_nulling_relids((Node *) root->append_rel_list,
2977  state2.inner_reduced,
2978  NULL);
2979  }
2980 
2981  /*
2982  * Partially-reduced full joins have to be done one at a time, since
2983  * they'll each need a different setting of except_relids.
2984  */
2985  foreach(lc, state2.partial_reduced)
2986  {
2988  Relids full_join_relids = bms_make_singleton(statep->full_join_rti);
2989 
2990  root->parse = (Query *)
2991  remove_nulling_relids((Node *) root->parse,
2992  full_join_relids,
2993  statep->unreduced_side);
2994  root->append_rel_list = (List *)
2995  remove_nulling_relids((Node *) root->append_rel_list,
2996  full_join_relids,
2997  statep->unreduced_side);
2998  }
2999 }
static void reduce_outer_joins_pass2(Node *jtnode, reduce_outer_joins_pass1_state *state1, reduce_outer_joins_pass2_state *state2, PlannerInfo *root, Relids nonnullable_rels, List *forced_null_vars)
static reduce_outer_joins_pass1_state * reduce_outer_joins_pass1(Node *jtnode)
Node * remove_nulling_relids(Node *node, const Bitmapset *removable_relids, const Bitmapset *except_relids)

References bms_is_empty, bms_make_singleton(), reduce_outer_joins_pass1_state::contains_outer, elog, ERROR, reduce_outer_joins_partial_state::full_join_rti, reduce_outer_joins_pass2_state::inner_reduced, lfirst, NIL, reduce_outer_joins_pass2_state::partial_reduced, reduce_outer_joins_pass1(), reduce_outer_joins_pass2(), remove_nulling_relids(), root, and reduce_outer_joins_partial_state::unreduced_side.

Referenced by subquery_planner().

◆ reduce_outer_joins_pass1()

static reduce_outer_joins_pass1_state * reduce_outer_joins_pass1 ( Node jtnode)
static

Definition at line 3007 of file prepjointree.c.

3008 {
3010 
3011  result = (reduce_outer_joins_pass1_state *)
3013  result->relids = NULL;
3014  result->contains_outer = false;
3015  result->sub_states = NIL;
3016 
3017  if (jtnode == NULL)
3018  return result;
3019  if (IsA(jtnode, RangeTblRef))
3020  {
3021  int varno = ((RangeTblRef *) jtnode)->rtindex;
3022 
3023  result->relids = bms_make_singleton(varno);
3024  }
3025  else if (IsA(jtnode, FromExpr))
3026  {
3027  FromExpr *f = (FromExpr *) jtnode;
3028  ListCell *l;
3029 
3030  foreach(l, f->fromlist)
3031  {
3032  reduce_outer_joins_pass1_state *sub_state;
3033 
3034  sub_state = reduce_outer_joins_pass1(lfirst(l));
3035  result->relids = bms_add_members(result->relids,
3036  sub_state->relids);
3037  result->contains_outer |= sub_state->contains_outer;
3038  result->sub_states = lappend(result->sub_states, sub_state);
3039  }
3040  }
3041  else if (IsA(jtnode, JoinExpr))
3042  {
3043  JoinExpr *j = (JoinExpr *) jtnode;
3044  reduce_outer_joins_pass1_state *sub_state;
3045 
3046  /* join's own RT index is not wanted in result->relids */
3047  if (IS_OUTER_JOIN(j->jointype))
3048  result->contains_outer = true;
3049 
3050  sub_state = reduce_outer_joins_pass1(j->larg);
3051  result->relids = bms_add_members(result->relids,
3052  sub_state->relids);
3053  result->contains_outer |= sub_state->contains_outer;
3054  result->sub_states = lappend(result->sub_states, sub_state);
3055 
3056  sub_state = reduce_outer_joins_pass1(j->rarg);
3057  result->relids = bms_add_members(result->relids,
3058  sub_state->relids);
3059  result->contains_outer |= sub_state->contains_outer;
3060  result->sub_states = lappend(result->sub_states, sub_state);
3061  }
3062  else
3063  elog(ERROR, "unrecognized node type: %d",
3064  (int) nodeTag(jtnode));
3065  return result;
3066 }
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:338

References bms_add_members(), bms_make_singleton(), reduce_outer_joins_pass1_state::contains_outer, elog, ERROR, FromExpr::fromlist, IS_OUTER_JOIN, IsA, j, lappend(), lfirst, NIL, nodeTag, palloc(), reduce_outer_joins_pass1_state::relids, and reduce_outer_joins_pass1_state::sub_states.

Referenced by reduce_outer_joins().

◆ reduce_outer_joins_pass2()

static void reduce_outer_joins_pass2 ( Node jtnode,
reduce_outer_joins_pass1_state state1,
reduce_outer_joins_pass2_state state2,
PlannerInfo root,
Relids  nonnullable_rels,
List forced_null_vars 
)
static

Definition at line 3085 of file prepjointree.c.

3091 {
3092  /*
3093  * pass 2 should never descend as far as an empty subnode or base rel,
3094  * because it's only called on subtrees marked as contains_outer.
3095  */
3096  if (jtnode == NULL)
3097  elog(ERROR, "reached empty jointree");
3098  if (IsA(jtnode, RangeTblRef))
3099  elog(ERROR, "reached base rel");
3100  else if (IsA(jtnode, FromExpr))
3101  {
3102  FromExpr *f = (FromExpr *) jtnode;
3103  ListCell *l;
3104  ListCell *s;
3105  Relids pass_nonnullable_rels;
3106  List *pass_forced_null_vars;
3107 
3108  /* Scan quals to see if we can add any constraints */
3109  pass_nonnullable_rels = find_nonnullable_rels(f->quals);
3110  pass_nonnullable_rels = bms_add_members(pass_nonnullable_rels,
3111  nonnullable_rels);
3112  pass_forced_null_vars = find_forced_null_vars(f->quals);
3113  pass_forced_null_vars = mbms_add_members(pass_forced_null_vars,
3114  forced_null_vars);
3115  /* And recurse --- but only into interesting subtrees */
3117  forboth(l, f->fromlist, s, state1->sub_states)
3118  {
3119  reduce_outer_joins_pass1_state *sub_state = lfirst(s);
3120 
3121  if (sub_state->contains_outer)
3122  reduce_outer_joins_pass2(lfirst(l), sub_state,
3123  state2, root,
3124  pass_nonnullable_rels,
3125  pass_forced_null_vars);
3126  }
3127  bms_free(pass_nonnullable_rels);
3128  /* can't so easily clean up var lists, unfortunately */
3129  }
3130  else if (IsA(jtnode, JoinExpr))
3131  {
3132  JoinExpr *j = (JoinExpr *) jtnode;
3133  int rtindex = j->rtindex;
3134  JoinType jointype = j->jointype;
3135  reduce_outer_joins_pass1_state *left_state = linitial(state1->sub_states);
3136  reduce_outer_joins_pass1_state *right_state = lsecond(state1->sub_states);
3137 
3138  /* Can we simplify this join? */
3139  switch (jointype)
3140  {
3141  case JOIN_INNER:
3142  break;
3143  case JOIN_LEFT:
3144  if (bms_overlap(nonnullable_rels, right_state->relids))
3145  jointype = JOIN_INNER;
3146  break;
3147  case JOIN_RIGHT:
3148  if (bms_overlap(nonnullable_rels, left_state->relids))
3149  jointype = JOIN_INNER;
3150  break;
3151  case JOIN_FULL:
3152  if (bms_overlap(nonnullable_rels, left_state->relids))
3153  {
3154  if (bms_overlap(nonnullable_rels, right_state->relids))
3155  jointype = JOIN_INNER;
3156  else
3157  {
3158  jointype = JOIN_LEFT;
3159  /* Also report partial reduction in state2 */
3160  report_reduced_full_join(state2, rtindex,
3161  right_state->relids);
3162  }
3163  }
3164  else
3165  {
3166  if (bms_overlap(nonnullable_rels, right_state->relids))
3167  {
3168  jointype = JOIN_RIGHT;
3169  /* Also report partial reduction in state2 */
3170  report_reduced_full_join(state2, rtindex,
3171  left_state->relids);
3172  }
3173  }
3174  break;
3175  case JOIN_SEMI:
3176  case JOIN_ANTI:
3177 
3178  /*
3179  * These could only have been introduced by pull_up_sublinks,
3180  * so there's no way that upper quals could refer to their
3181  * righthand sides, and no point in checking. We don't expect
3182  * to see JOIN_RIGHT_SEMI or JOIN_RIGHT_ANTI yet.
3183  */
3184  break;
3185  default:
3186  elog(ERROR, "unrecognized join type: %d",
3187  (int) jointype);
3188  break;
3189  }
3190 
3191  /*
3192  * Convert JOIN_RIGHT to JOIN_LEFT. Note that in the case where we
3193  * reduced JOIN_FULL to JOIN_RIGHT, this will mean the JoinExpr no
3194  * longer matches the internal ordering of any CoalesceExpr's built to
3195  * represent merged join variables. We don't care about that at
3196  * present, but be wary of it ...
3197  */
3198  if (jointype == JOIN_RIGHT)
3199  {
3200  Node *tmparg;
3201 
3202  tmparg = j->larg;
3203  j->larg = j->rarg;
3204  j->rarg = tmparg;
3205  jointype = JOIN_LEFT;
3206  right_state = linitial(state1->sub_states);
3207  left_state = lsecond(state1->sub_states);
3208  }
3209 
3210  /*
3211  * See if we can reduce JOIN_LEFT to JOIN_ANTI. This is the case if
3212  * the join's own quals are strict for any var that was forced null by
3213  * higher qual levels. NOTE: there are other ways that we could
3214  * detect an anti-join, in particular if we were to check whether Vars
3215  * coming from the RHS must be non-null because of table constraints.
3216  * That seems complicated and expensive though (in particular, one
3217  * would have to be wary of lower outer joins). For the moment this
3218  * seems sufficient.
3219  */
3220  if (jointype == JOIN_LEFT)
3221  {
3222  List *nonnullable_vars;
3223  Bitmapset *overlap;
3224 
3225  /* Find Vars in j->quals that must be non-null in joined rows */
3226  nonnullable_vars = find_nonnullable_vars(j->quals);
3227 
3228  /*
3229  * It's not sufficient to check whether nonnullable_vars and
3230  * forced_null_vars overlap: we need to know if the overlap
3231  * includes any RHS variables.
3232  */
3233  overlap = mbms_overlap_sets(nonnullable_vars, forced_null_vars);
3234  if (bms_overlap(overlap, right_state->relids))
3235  jointype = JOIN_ANTI;
3236  }
3237 
3238  /*
3239  * Apply the jointype change, if any, to both jointree node and RTE.
3240  * Also, if we changed an RTE to INNER, add its RTI to inner_reduced.
3241  */
3242  if (rtindex && jointype != j->jointype)
3243  {
3244  RangeTblEntry *rte = rt_fetch(rtindex, root->parse->rtable);
3245 
3246  Assert(rte->rtekind == RTE_JOIN);
3247  Assert(rte->jointype == j->jointype);
3248  rte->jointype = jointype;
3249  if (jointype == JOIN_INNER)
3250  state2->inner_reduced = bms_add_member(state2->inner_reduced,
3251  rtindex);
3252  }
3253  j->jointype = jointype;
3254 
3255  /* Only recurse if there's more to do below here */
3256  if (left_state->contains_outer || right_state->contains_outer)
3257  {
3258  Relids local_nonnullable_rels;
3259  List *local_forced_null_vars;
3260  Relids pass_nonnullable_rels;
3261  List *pass_forced_null_vars;
3262 
3263  /*
3264  * If this join is (now) inner, we can add any constraints its
3265  * quals provide to those we got from above. But if it is outer,
3266  * we can pass down the local constraints only into the nullable
3267  * side, because an outer join never eliminates any rows from its
3268  * non-nullable side. Also, there is no point in passing upper
3269  * constraints into the nullable side, since if there were any
3270  * we'd have been able to reduce the join. (In the case of upper
3271  * forced-null constraints, we *must not* pass them into the
3272  * nullable side --- they either applied here, or not.) The upshot
3273  * is that we pass either the local or the upper constraints,
3274  * never both, to the children of an outer join.
3275  *
3276  * Note that a SEMI join works like an inner join here: it's okay
3277  * to pass down both local and upper constraints. (There can't be
3278  * any upper constraints affecting its inner side, but it's not
3279  * worth having a separate code path to avoid passing them.)
3280  *
3281  * At a FULL join we just punt and pass nothing down --- is it
3282  * possible to be smarter?
3283  */
3284  if (jointype != JOIN_FULL)
3285  {
3286  local_nonnullable_rels = find_nonnullable_rels(j->quals);
3287  local_forced_null_vars = find_forced_null_vars(j->quals);
3288  if (jointype == JOIN_INNER || jointype == JOIN_SEMI)
3289  {
3290  /* OK to merge upper and local constraints */
3291  local_nonnullable_rels = bms_add_members(local_nonnullable_rels,
3292  nonnullable_rels);
3293  local_forced_null_vars = mbms_add_members(local_forced_null_vars,
3294  forced_null_vars);
3295  }
3296  }
3297  else
3298  {
3299  /* no use in calculating these */
3300  local_nonnullable_rels = NULL;
3301  local_forced_null_vars = NIL;
3302  }
3303 
3304  if (left_state->contains_outer)
3305  {
3306  if (jointype == JOIN_INNER || jointype == JOIN_SEMI)
3307  {
3308  /* pass union of local and upper constraints */
3309  pass_nonnullable_rels = local_nonnullable_rels;
3310  pass_forced_null_vars = local_forced_null_vars;
3311  }
3312  else if (jointype != JOIN_FULL) /* ie, LEFT or ANTI */
3313  {
3314  /* can't pass local constraints to non-nullable side */
3315  pass_nonnullable_rels = nonnullable_rels;
3316  pass_forced_null_vars = forced_null_vars;
3317  }
3318  else
3319  {
3320  /* no constraints pass through JOIN_FULL */
3321  pass_nonnullable_rels = NULL;
3322  pass_forced_null_vars = NIL;
3323  }
3324  reduce_outer_joins_pass2(j->larg, left_state,
3325  state2, root,
3326  pass_nonnullable_rels,
3327  pass_forced_null_vars);
3328  }
3329 
3330  if (right_state->contains_outer)
3331  {
3332  if (jointype != JOIN_FULL) /* ie, INNER/LEFT/SEMI/ANTI */
3333  {
3334  /* pass appropriate constraints, per comment above */
3335  pass_nonnullable_rels = local_nonnullable_rels;
3336  pass_forced_null_vars = local_forced_null_vars;
3337  }
3338  else
3339  {
3340  /* no constraints pass through JOIN_FULL */
3341  pass_nonnullable_rels = NULL;
3342  pass_forced_null_vars = NIL;
3343  }
3344  reduce_outer_joins_pass2(j->rarg, right_state,
3345  state2, root,
3346  pass_nonnullable_rels,
3347  pass_forced_null_vars);
3348  }
3349  bms_free(local_nonnullable_rels);
3350  }
3351  }
3352  else
3353  elog(ERROR, "unrecognized node type: %d",
3354  (int) nodeTag(jtnode));
3355 }
void bms_free(Bitmapset *a)
Definition: bitmapset.c:239
List * find_forced_null_vars(Node *node)
Definition: clauses.c:1915
Relids find_nonnullable_rels(Node *clause)
Definition: clauses.c:1455
List * find_nonnullable_vars(Node *clause)
Definition: clauses.c:1706
Bitmapset * mbms_overlap_sets(const List *a, const List *b)
List * mbms_add_members(List *a, const List *b)
JoinType
Definition: nodes.h:288
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
#define lsecond(l)
Definition: pg_list.h:183
static void report_reduced_full_join(reduce_outer_joins_pass2_state *state2, int rtindex, Relids relids)
JoinType jointype
Definition: parsenodes.h:1151

References Assert, bms_add_member(), bms_add_members(), bms_free(), bms_overlap(), reduce_outer_joins_pass1_state::contains_outer, elog, ERROR, find_forced_null_vars(), find_nonnullable_rels(), find_nonnullable_vars(), forboth, FromExpr::fromlist, reduce_outer_joins_pass2_state::inner_reduced, IsA, j, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, RangeTblEntry::jointype, lfirst, linitial, list_length(), lsecond, mbms_add_members(), mbms_overlap_sets(), NIL, nodeTag, FromExpr::quals, reduce_outer_joins_pass1_state::relids, report_reduced_full_join(), root, rt_fetch, RTE_JOIN, RangeTblEntry::rtekind, and reduce_outer_joins_pass1_state::sub_states.

Referenced by reduce_outer_joins().

◆ remove_result_refs()

static void remove_result_refs ( PlannerInfo root,
int  varno,
Node newjtloc 
)
static

Definition at line 3802 of file prepjointree.c.

3803 {
3804  /* Fix up PlaceHolderVars as needed */
3805  /* If there are no PHVs anywhere, we can skip this bit */
3806  if (root->glob->lastPHId != 0)
3807  {
3808  Relids subrelids;
3809 
3810  subrelids = get_relids_in_jointree(newjtloc, true, false);
3811  Assert(!bms_is_empty(subrelids));
3812  substitute_phv_relids((Node *) root->parse, varno, subrelids);
3813  fix_append_rel_relids(root, varno, subrelids);
3814  }
3815 
3816  /*
3817  * We also need to remove any PlanRowMark referencing the RTE, but we
3818  * postpone that work until we return to remove_useless_result_rtes.
3819  */
3820 }

References Assert, bms_is_empty, fix_append_rel_relids(), get_relids_in_jointree(), root, and substitute_phv_relids().

Referenced by remove_useless_results_recurse().

◆ remove_useless_result_rtes()

void remove_useless_result_rtes ( PlannerInfo root)

Definition at line 3428 of file prepjointree.c.

3429 {
3430  Relids dropped_outer_joins = NULL;
3431  ListCell *cell;
3432 
3433  /* Top level of jointree must always be a FromExpr */
3434  Assert(IsA(root->parse->jointree, FromExpr));
3435  /* Recurse ... */
3436  root->parse->jointree = (FromExpr *)
3438  (Node *) root->parse->jointree,
3439  NULL,
3440  &dropped_outer_joins);
3441  /* We should still have a FromExpr */
3442  Assert(IsA(root->parse->jointree, FromExpr));
3443 
3444  /*
3445  * If we removed any outer-join nodes from the jointree, run around and
3446  * remove references to those joins as nulling rels. (There could be such
3447  * references in PHVs that we pulled up out of the original subquery that
3448  * the RESULT rel replaced. This is kosher on the grounds that we now
3449  * know that such an outer join wouldn't really have nulled anything.) We
3450  * don't do this during the main recursion, for simplicity and because we
3451  * can handle all such joins in a single pass over the parse tree.
3452  */
3453  if (!bms_is_empty(dropped_outer_joins))
3454  {
3455  root->parse = (Query *)
3456  remove_nulling_relids((Node *) root->parse,
3457  dropped_outer_joins,
3458  NULL);
3459  /* There could be references in the append_rel_list, too */
3460  root->append_rel_list = (List *)
3461  remove_nulling_relids((Node *) root->append_rel_list,
3462  dropped_outer_joins,
3463  NULL);
3464  }
3465 
3466  /*
3467  * Remove any PlanRowMark referencing an RTE_RESULT RTE. We obviously
3468  * must do that for any RTE_RESULT that we just removed. But one for a
3469  * RTE that we did not remove can be dropped anyway: since the RTE has
3470  * only one possible output row, there is no need for EPQ to mark and
3471  * restore that row.
3472  *
3473  * It's necessary, not optional, to remove the PlanRowMark for a surviving
3474  * RTE_RESULT RTE; otherwise we'll generate a whole-row Var for the
3475  * RTE_RESULT, which the executor has no support for.
3476  */
3477  foreach(cell, root->rowMarks)
3478  {
3479  PlanRowMark *rc = (PlanRowMark *) lfirst(cell);
3480 
3481  if (rt_fetch(rc->rti, root->parse->rtable)->rtekind == RTE_RESULT)
3482  root->rowMarks = foreach_delete_current(root->rowMarks, cell);
3483  }
3484 }
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
static Node * remove_useless_results_recurse(PlannerInfo *root, Node *jtnode, Node **parent_quals, Relids *dropped_outer_joins)

References Assert, bms_is_empty, foreach_delete_current, IsA, lfirst, remove_nulling_relids(), remove_useless_results_recurse(), root, rt_fetch, RTE_RESULT, and PlanRowMark::rti.

Referenced by subquery_planner().

◆ remove_useless_results_recurse()

static Node * remove_useless_results_recurse ( PlannerInfo root,
Node jtnode,
Node **  parent_quals,
Relids dropped_outer_joins 
)
static

Definition at line 3501 of file prepjointree.c.

3504 {
3505  Assert(jtnode != NULL);
3506  if (IsA(jtnode, RangeTblRef))
3507  {
3508  /* Can't immediately do anything with a RangeTblRef */
3509  }
3510  else if (IsA(jtnode, FromExpr))
3511  {
3512  FromExpr *f = (FromExpr *) jtnode;
3513  Relids result_relids = NULL;
3514  ListCell *cell;
3515 
3516  /*
3517  * We can drop RTE_RESULT rels from the fromlist so long as at least
3518  * one child remains, since joining to a one-row table changes
3519  * nothing. (But we can't drop a RTE_RESULT that computes PHV(s) that
3520  * are needed by some sibling. The cleanup transformation below would
3521  * reassign the PHVs to be computed at the join, which is too late for
3522  * the sibling's use.) The easiest way to mechanize this rule is to
3523  * modify the list in-place.
3524  */
3525  foreach(cell, f->fromlist)
3526  {
3527  Node *child = (Node *) lfirst(cell);
3528  int varno;
3529 
3530  /* Recursively transform child, allowing it to push up quals ... */
3531  child = remove_useless_results_recurse(root, child,
3532  &f->quals,
3533  dropped_outer_joins);
3534  /* ... and stick it back into the tree */
3535  lfirst(cell) = child;
3536 
3537  /*
3538  * If it's an RTE_RESULT with at least one sibling, and no sibling
3539  * references dependent PHVs, we can drop it. We don't yet know
3540  * what the inner join's final relid set will be, so postpone
3541  * cleanup of PHVs etc till after this loop.
3542  */
3543  if (list_length(f->fromlist) > 1 &&
3544  (varno = get_result_relid(root, child)) != 0 &&
3545  !find_dependent_phvs_in_jointree(root, (Node *) f, varno))
3546  {
3547  f->fromlist = foreach_delete_current(f->fromlist, cell);
3548  result_relids = bms_add_member(result_relids, varno);
3549  }
3550  }
3551 
3552  /*
3553  * Clean up if we dropped any RTE_RESULT RTEs. This is a bit
3554  * inefficient if there's more than one, but it seems better to
3555  * optimize the support code for the single-relid case.
3556  */
3557  if (result_relids)
3558  {
3559  int varno = -1;
3560 
3561  while ((varno = bms_next_member(result_relids, varno)) >= 0)
3562  remove_result_refs(root, varno, (Node *) f);
3563  }
3564 
3565  /*
3566  * If the FromExpr now has only one child, see if we can elide it.
3567  * This is always valid if there are no quals, except at the top of
3568  * the jointree (since Query.jointree is required to point to a
3569  * FromExpr). Otherwise, we can do it if we can push the quals up to
3570  * the parent node.
3571  *
3572  * Note: while it would not be terribly hard to generalize this
3573  * transformation to merge multi-child FromExprs into their parent
3574  * FromExpr, that risks making the parent join too expensive to plan.
3575  * We leave it to later processing to decide heuristically whether
3576  * that's a good idea. Pulling up a single child is always OK,
3577  * however.
3578  */
3579  if (list_length(f->fromlist) == 1 &&
3580  f != root->parse->jointree &&
3581  (f->quals == NULL || parent_quals != NULL))
3582  {
3583  /*
3584  * Merge any quals up to parent. They should be in implicit-AND
3585  * format by now, so we just need to concatenate lists. Put the
3586  * child quals at the front, on the grounds that they should
3587  * nominally be evaluated earlier.
3588  */
3589  if (f->quals != NULL)
3590  *parent_quals = (Node *)
3592  castNode(List, *parent_quals));
3593  return (Node *) linitial(f->fromlist);
3594  }
3595  }
3596  else if (IsA(jtnode, JoinExpr))
3597  {
3598  JoinExpr *j = (JoinExpr *) jtnode;
3599  int varno;
3600 
3601  /*
3602  * First, recurse. We can absorb pushed-up FromExpr quals from either
3603  * child into this node if the jointype is INNER, since then this is
3604  * equivalent to a FromExpr. When the jointype is LEFT, we can absorb
3605  * quals from the RHS child into the current node, as they're
3606  * essentially degenerate quals of the outer join. Moreover, if we've
3607  * been passed down a parent_quals pointer then we can allow quals of
3608  * the LHS child to be absorbed into the parent. (This is important
3609  * to ensure we remove single-child FromExprs immediately below
3610  * commutable left joins.) For other jointypes, we can't move child
3611  * quals up, or at least there's no particular reason to.
3612  */
3613  j->larg = remove_useless_results_recurse(root, j->larg,
3614  (j->jointype == JOIN_INNER) ?
3615  &j->quals :
3616  (j->jointype == JOIN_LEFT) ?
3617  parent_quals : NULL,
3618  dropped_outer_joins);
3619  j->rarg = remove_useless_results_recurse(root, j->rarg,
3620  (j->jointype == JOIN_INNER ||
3621  j->jointype == JOIN_LEFT) ?
3622  &j->quals : NULL,
3623  dropped_outer_joins);
3624 
3625  /* Apply join-type-specific optimization rules */
3626  switch (j->jointype)
3627  {
3628  case JOIN_INNER:
3629 
3630  /*
3631  * An inner join is equivalent to a FromExpr, so if either
3632  * side was simplified to an RTE_RESULT rel, we can replace
3633  * the join with a FromExpr with just the other side.
3634  * Furthermore, we can elide that FromExpr according to the
3635  * same rules as above.
3636  *
3637  * Just as in the FromExpr case, we can't simplify if the
3638  * other input rel references any PHVs that are marked as to
3639  * be evaluated at the RTE_RESULT rel, because we can't
3640  * postpone their evaluation in that case. But we only have
3641  * to check this in cases where it's syntactically legal for
3642  * the other input to have a LATERAL reference to the
3643  * RTE_RESULT rel. Only RHSes of inner and left joins are
3644  * allowed to have such refs.
3645  */
3646  if ((varno = get_result_relid(root, j->larg)) != 0 &&
3647  !find_dependent_phvs_in_jointree(root, j->rarg, varno))
3648  {
3649  remove_result_refs(root, varno, j->rarg);
3650  if (j->quals != NULL && parent_quals == NULL)
3651  jtnode = (Node *)
3652  makeFromExpr(list_make1(j->rarg), j->quals);
3653  else
3654  {
3655  /* Merge any quals up to parent */
3656  if (j->quals != NULL)
3657  *parent_quals = (Node *)
3658  list_concat(castNode(List, j->quals),
3659  castNode(List, *parent_quals));
3660  jtnode = j->rarg;
3661  }
3662  }
3663  else if ((varno = get_result_relid(root, j->rarg)) != 0)
3664  {
3665  remove_result_refs(root, varno, j->larg);
3666  if (j->quals != NULL && parent_quals == NULL)
3667  jtnode = (Node *)
3668  makeFromExpr(list_make1(j->larg), j->quals);
3669  else
3670  {
3671  /* Merge any quals up to parent */
3672  if (j->quals != NULL)
3673  *parent_quals = (Node *)
3674  list_concat(castNode(List, j->quals),
3675  castNode(List, *parent_quals));
3676  jtnode = j->larg;
3677  }
3678  }
3679  break;
3680  case JOIN_LEFT:
3681 
3682  /*
3683  * We can simplify this case if the RHS is an RTE_RESULT, with
3684  * two different possibilities:
3685  *
3686  * If the qual is empty (JOIN ON TRUE), then the join can be
3687  * strength-reduced to a plain inner join, since each LHS row
3688  * necessarily has exactly one join partner. So we can always
3689  * discard the RHS, much as in the JOIN_INNER case above.
3690  * (Again, the LHS could not contain a lateral reference to
3691  * the RHS.)
3692  *
3693  * Otherwise, it's still true that each LHS row should be
3694  * returned exactly once, and since the RHS returns no columns
3695  * (unless there are PHVs that have to be evaluated there), we
3696  * don't much care if it's null-extended or not. So in this
3697  * case also, we can just ignore the qual and discard the left
3698  * join.
3699  */
3700  if ((varno = get_result_relid(root, j->rarg)) != 0 &&
3701  (j->quals == NULL ||
3702  !find_dependent_phvs(root, varno)))
3703  {
3704  remove_result_refs(root, varno, j->larg);
3705  *dropped_outer_joins = bms_add_member(*dropped_outer_joins,
3706  j->rtindex);
3707  jtnode = j->larg;
3708  }
3709  break;
3710  case JOIN_SEMI:
3711 
3712  /*
3713  * We may simplify this case if the RHS is an RTE_RESULT; the
3714  * join qual becomes effectively just a filter qual for the
3715  * LHS, since we should either return the LHS row or not. The
3716  * filter clause must go into a new FromExpr if we can't push
3717  * it up to the parent.
3718  *
3719  * There is a fine point about PHVs that are supposed to be
3720  * evaluated at the RHS. Such PHVs could only appear in the
3721  * semijoin's qual, since the rest of the query cannot
3722  * reference any outputs of the semijoin's RHS. Therefore,
3723  * they can't actually go to null before being examined, and
3724  * it'd be OK to just remove the PHV wrapping. We don't have
3725  * infrastructure for that, but remove_result_refs() will
3726  * relabel them as to be evaluated at the LHS, which is fine.
3727  *
3728  * Also, we don't need to worry about removing traces of the
3729  * join's rtindex, since it hasn't got one.
3730  */
3731  if ((varno = get_result_relid(root, j->rarg)) != 0)
3732  {
3733  Assert(j->rtindex == 0);
3734  remove_result_refs(root, varno, j->larg);
3735  if (j->quals != NULL && parent_quals == NULL)
3736  jtnode = (Node *)
3737  makeFromExpr(list_make1(j->larg), j->quals);
3738  else
3739  {
3740  /* Merge any quals up to parent */
3741  if (j->quals != NULL)
3742  *parent_quals = (Node *)
3743  list_concat(castNode(List, j->quals),
3744  castNode(List, *parent_quals));
3745  jtnode = j->larg;
3746  }
3747  }
3748  break;
3749  case JOIN_FULL:
3750  case JOIN_ANTI:
3751  /* We have no special smarts for these cases */
3752  break;
3753  default:
3754  /* Note: JOIN_RIGHT should be gone at this point */
3755  elog(ERROR, "unrecognized join type: %d",
3756  (int) j->jointype);
3757  break;
3758  }
3759  }
3760  else
3761  elog(ERROR, "unrecognized node type: %d",
3762  (int) nodeTag(jtnode));
3763  return jtnode;
3764 }
static void remove_result_refs(PlannerInfo *root, int varno, Node *newjtloc)
static int get_result_relid(PlannerInfo *root, Node *jtnode)
static bool find_dependent_phvs(PlannerInfo *root, int varno)
static bool find_dependent_phvs_in_jointree(PlannerInfo *root, Node *node, int varno)

References Assert, bms_add_member(), bms_next_member(), castNode, elog, ERROR, find_dependent_phvs(), find_dependent_phvs_in_jointree(), foreach_delete_current, FromExpr::fromlist, get_result_relid(), IsA, j, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_SEMI, lfirst, linitial, list_concat(), list_length(), list_make1, makeFromExpr(), nodeTag, FromExpr::quals, remove_result_refs(), and root.

Referenced by remove_useless_result_rtes().

◆ replace_empty_jointree()

void replace_empty_jointree ( Query parse)

Definition at line 395 of file prepjointree.c.

396 {
397  RangeTblEntry *rte;
398  Index rti;
399  RangeTblRef *rtr;
400 
401  /* Nothing to do if jointree is already nonempty */
402  if (parse->jointree->fromlist != NIL)
403  return;
404 
405  /* We mustn't change it in the top level of a setop tree, either */
406  if (parse->setOperations)
407  return;
408 
409  /* Create suitable RTE */
410  rte = makeNode(RangeTblEntry);
411  rte->rtekind = RTE_RESULT;
412  rte->eref = makeAlias("*RESULT*", NIL);
413 
414  /* Add it to rangetable */
415  parse->rtable = lappend(parse->rtable, rte);
416  rti = list_length(parse->rtable);
417 
418  /* And jam a reference into the jointree */
419  rtr = makeNode(RangeTblRef);
420  rtr->rtindex = rti;
421  parse->jointree->fromlist = list_make1(rtr);
422 }
unsigned int Index
Definition: c.h:593

References lappend(), list_length(), list_make1, makeAlias(), makeNode, NIL, parse(), RTE_RESULT, RangeTblEntry::rtekind, and RangeTblRef::rtindex.

Referenced by convert_EXISTS_sublink_to_join(), pull_up_simple_subquery(), and subquery_planner().

◆ replace_vars_in_jointree()

static void replace_vars_in_jointree ( Node jtnode,
pullup_replace_vars_context context 
)
static

Definition at line 2368 of file prepjointree.c.

2370 {
2371  if (jtnode == NULL)
2372  return;
2373  if (IsA(jtnode, RangeTblRef))
2374  {
2375  /*
2376  * If the RangeTblRef refers to a LATERAL subquery (that isn't the
2377  * same subquery we're pulling up), it might contain references to the
2378  * target subquery, which we must replace. We drive this from the
2379  * jointree scan, rather than a scan of the rtable, so that we can
2380  * avoid processing no-longer-referenced RTEs.
2381  */
2382  int varno = ((RangeTblRef *) jtnode)->rtindex;
2383 
2384  if (varno != context->varno) /* ignore target subquery itself */
2385  {
2386  RangeTblEntry *rte = rt_fetch(varno, context->root->parse->rtable);
2387 
2388  Assert(rte != context->target_rte);
2389  if (rte->lateral)
2390  {
2391  switch (rte->rtekind)
2392  {
2393  case RTE_RELATION:
2394  /* shouldn't be marked LATERAL unless tablesample */
2395  Assert(rte->tablesample);
2396  rte->tablesample = (TableSampleClause *)
2398  context);
2399  break;
2400  case RTE_SUBQUERY:
2401  rte->subquery =
2403  context);
2404  break;
2405  case RTE_FUNCTION:
2406  rte->functions = (List *)
2408  context);
2409  break;
2410  case RTE_TABLEFUNC:
2411  rte->tablefunc = (TableFunc *)
2413  context);
2414  break;
2415  case RTE_VALUES:
2416  rte->values_lists = (List *)
2418  context);
2419  break;
2420  case RTE_JOIN:
2421  case RTE_CTE:
2422  case RTE_NAMEDTUPLESTORE:
2423  case RTE_RESULT:
2424  case RTE_GROUP:
2425  /* these shouldn't be marked LATERAL */
2426  Assert(false);
2427  break;
2428  }
2429  }
2430  }
2431  }
2432  else if (IsA(jtnode, FromExpr))
2433  {
2434  FromExpr *f = (FromExpr *) jtnode;
2435  ListCell *l;
2436 
2437  foreach(l, f->fromlist)
2440  }
2441  else if (IsA(jtnode, JoinExpr))
2442  {
2443  JoinExpr *j = (JoinExpr *) jtnode;
2444  bool save_wrap_non_vars = context->wrap_non_vars;
2445 
2448 
2449  /*
2450  * Use PHVs within the join quals of a full join. Otherwise, we
2451  * cannot identify which side of the join a pulled-up var-free
2452  * expression came from, which can lead to failure to make a plan at
2453  * all because none of the quals appear to be mergeable or hashable
2454  * conditions.
2455  */
2456  if (j->jointype == JOIN_FULL)
2457  context->wrap_non_vars = true;
2458 
2459  j->quals = pullup_replace_vars(j->quals, context);
2460 
2461  context->wrap_non_vars = save_wrap_non_vars;
2462  }
2463  else
2464  elog(ERROR, "unrecognized node type: %d",
2465  (int) nodeTag(jtnode));
2466 }
static Query * pullup_replace_vars_subquery(Query *query, pullup_replace_vars_context *context)
TableFunc * tablefunc
Definition: parsenodes.h:1184

References Assert, context, elog, ERROR, FromExpr::fromlist, RangeTblEntry::functions, IsA, j, JOIN_FULL, lfirst, nodeTag, pullup_replace_vars(), pullup_replace_vars_subquery(), FromExpr::quals, rt_fetch, RTE_CTE, RTE_FUNCTION, RTE_GROUP, RTE_JOIN, RTE_NAMEDTUPLESTORE, RTE_RELATION, RTE_RESULT, RTE_SUBQUERY, RTE_TABLEFUNC, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::subquery, RangeTblEntry::tablefunc, RangeTblEntry::tablesample, and RangeTblEntry::values_lists.

Referenced by perform_pullup_replace_vars().

◆ report_reduced_full_join()

static void report_reduced_full_join ( reduce_outer_joins_pass2_state state2,
int  rtindex,
Relids  relids 
)
static

Definition at line 3359 of file prepjointree.c.

3361 {
3363 
3364  statep = palloc(sizeof(reduce_outer_joins_partial_state));
3365  statep->full_join_rti = rtindex;
3366  statep->unreduced_side = relids;
3367  state2->partial_reduced = lappend(state2->partial_reduced, statep);
3368 }

References reduce_outer_joins_partial_state::full_join_rti, lappend(), palloc(), reduce_outer_joins_pass2_state::partial_reduced, and reduce_outer_joins_partial_state::unreduced_side.

Referenced by reduce_outer_joins_pass2().

◆ substitute_phv_relids()

static void substitute_phv_relids ( Node node,
int  varno,
Relids  subrelids 
)
static

Definition at line 4002 of file prepjointree.c.

4003 {
4005 
4006  context.varno = varno;
4007  context.sublevels_up = 0;
4008  context.subrelids = subrelids;
4009 
4010  /*
4011  * Must be prepared to start with a Query or a bare expression tree.
4012  */
4015  &context,
4016  0);
4017 }
#define query_or_expression_tree_walker(n, w, c, f)
Definition: nodeFuncs.h:171
static bool substitute_phv_relids_walker(Node *node, substitute_phv_relids_context *context)

References context, query_or_expression_tree_walker, and substitute_phv_relids_walker().

Referenced by fix_append_rel_relids(), pull_up_simple_subquery(), and remove_result_refs().

◆ substitute_phv_relids_walker()

static bool substitute_phv_relids_walker ( Node node,
substitute_phv_relids_context context 
)
static

Definition at line 3959 of file prepjointree.c.

3961 {
3962  if (node == NULL)
3963  return false;
3964  if (IsA(node, PlaceHolderVar))
3965  {
3966  PlaceHolderVar *phv = (PlaceHolderVar *) node;
3967 
3968  if (phv->phlevelsup == context->sublevels_up &&
3969  bms_is_member(context->varno, phv->phrels))
3970  {
3971  phv->phrels = bms_union(phv->phrels,
3972  context->subrelids);
3973  phv->phrels = bms_del_member(phv->phrels,
3974  context->varno);
3975  /* Assert we haven't broken the PHV */
3976  Assert(!bms_is_empty(phv->phrels));
3977  }
3978  /* fall through to examine children */
3979  }
3980  if (IsA(node, Query))
3981  {
3982  /* Recurse into subselects */
3983  bool result;
3984 
3985  context->sublevels_up++;
3986  result = query_tree_walker((Query *) node,
3988  context, 0);
3989  context->sublevels_up--;
3990  return result;
3991  }
3992  /* Shouldn't need to handle planner auxiliary nodes here */
3993  Assert(!IsA(node, SpecialJoinInfo));
3994  Assert(!IsA(node, AppendRelInfo));
3995  Assert(!IsA(node, PlaceHolderInfo));
3996  Assert(!IsA(node, MinMaxAggInfo));
3997 
3999 }
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:868

References Assert, bms_del_member(), bms_is_empty, bms_is_member(), bms_union(), context, expression_tree_walker, IsA, PlaceHolderVar::phlevelsup, and query_tree_walker.

Referenced by substitute_phv_relids().

◆ transform_MERGE_to_join()

void transform_MERGE_to_join ( Query parse)

Definition at line 168 of file prepjointree.c.

169 {
170  RangeTblEntry *joinrte;
171  JoinExpr *joinexpr;
172  bool have_action[NUM_MERGE_MATCH_KINDS];
173  JoinType jointype;
174  int joinrti;
175  List *vars;
176  RangeTblRef *rtr;
177  FromExpr *target;
178  Node *source;
179  int sourcerti;
180 
181  if (parse->commandType != CMD_MERGE)
182  return;
183 
184  /* XXX probably bogus */
185  vars = NIL;
186 
187  /*
188  * Work out what kind of join is required. If there any WHEN NOT MATCHED
189  * BY SOURCE/TARGET actions, an outer join is required so that we process
190  * all unmatched tuples from the source and/or target relations.
191  * Otherwise, we can use an inner join.
192  */
193  have_action[MERGE_WHEN_MATCHED] = false;
194  have_action[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] = false;
195  have_action[MERGE_WHEN_NOT_MATCHED_BY_TARGET] = false;
196 
197  foreach_node(MergeAction, action, parse->mergeActionList)
198  {
199  if (action->commandType != CMD_NOTHING)
200  have_action[action->matchKind] = true;
201  }
202 
203  if (have_action[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] &&
205  jointype = JOIN_FULL;
206  else if (have_action[MERGE_WHEN_NOT_MATCHED_BY_SOURCE])
207  jointype = JOIN_LEFT;
208  else if (have_action[MERGE_WHEN_NOT_MATCHED_BY_TARGET])
209  jointype = JOIN_RIGHT;
210  else
211  jointype = JOIN_INNER;
212 
213  /* Manufacture a join RTE to use. */
214  joinrte = makeNode(RangeTblEntry);
215  joinrte->rtekind = RTE_JOIN;
216  joinrte->jointype = jointype;
217  joinrte->joinmergedcols = 0;
218  joinrte->joinaliasvars = vars;
219  joinrte->joinleftcols = NIL; /* MERGE does not allow JOIN USING */
220  joinrte->joinrightcols = NIL; /* ditto */
221  joinrte->join_using_alias = NULL;
222 
223  joinrte->alias = NULL;
224  joinrte->eref = makeAlias("*MERGE*", NIL);
225  joinrte->lateral = false;
226  joinrte->inh = false;
227  joinrte->inFromCl = true;
228 
229  /*
230  * Add completed RTE to pstate's range table list, so that we know its
231  * index.
232  */
233  parse->rtable = lappend(parse->rtable, joinrte);
234  joinrti = list_length(parse->rtable);
235 
236  /*
237  * Create a JOIN between the target and the source relation.
238  *
239  * Here the target is identified by parse->mergeTargetRelation. For a
240  * regular table, this will equal parse->resultRelation, but for a
241  * trigger-updatable view, it will be the expanded view subquery that we
242  * need to pull data from.
243  *
244  * The source relation is in parse->jointree->fromlist, but any quals in
245  * parse->jointree->quals are restrictions on the target relation (if the
246  * target relation is an auto-updatable view).
247  */
248  /* target rel, with any quals */
249  rtr = makeNode(RangeTblRef);
250  rtr->rtindex = parse->mergeTargetRelation;
251  target = makeFromExpr(list_make1(rtr), parse->jointree->quals);
252 
253  /* source rel (expect exactly one -- see transformMergeStmt()) */
254  Assert(list_length(parse->jointree->fromlist) == 1);
255  source = linitial(parse->jointree->fromlist);
256 
257  /*
258  * index of source rel (expect either a RangeTblRef or a JoinExpr -- see
259  * transformFromClauseItem()).
260  */
261  if (IsA(source, RangeTblRef))
262  sourcerti = ((RangeTblRef *) source)->rtindex;
263  else if (IsA(source, JoinExpr))
264  sourcerti = ((JoinExpr *) source)->rtindex;
265  else
266  {
267  elog(ERROR, "unrecognized source node type: %d",
268  (int) nodeTag(source));
269  sourcerti = 0; /* keep compiler quiet */
270  }
271 
272  /* Join the source and target */
273  joinexpr = makeNode(JoinExpr);
274  joinexpr->jointype = jointype;
275  joinexpr->isNatural = false;
276  joinexpr->larg = (Node *) target;
277  joinexpr->rarg = source;
278  joinexpr->usingClause = NIL;
279  joinexpr->join_using_alias = NULL;
280  joinexpr->quals = parse->mergeJoinCondition;
281  joinexpr->alias = NULL;
282  joinexpr->rtindex = joinrti;
283 
284  /* Make the new join be the sole entry in the query's jointree */
285  parse->jointree->fromlist = list_make1(joinexpr);
286  parse->jointree->quals = NULL;
287 
288  /*
289  * If necessary, mark parse->targetlist entries that refer to the target
290  * as nullable by the join. Normally the targetlist will be empty for a
291  * MERGE, but if the target is a trigger-updatable view, it will contain a
292  * whole-row Var referring to the expanded view query.
293  */
294  if (parse->targetList != NIL &&
295  (jointype == JOIN_RIGHT || jointype == JOIN_FULL))
296  parse->targetList = (List *)
297  add_nulling_relids((Node *) parse->targetList,
298  bms_make_singleton(parse->mergeTargetRelation),
299  bms_make_singleton(joinrti));
300 
301  /*
302  * If the source relation is on the outer side of the join, mark any
303  * source relation Vars in the join condition, actions, and RETURNING list
304  * as nullable by the join. These Vars will be added to the targetlist by
305  * preprocess_targetlist(), so it's important to mark them correctly here.
306  *
307  * It might seem that this is not necessary for Vars in the join
308  * condition, since it is inside the join, but it is also needed above the
309  * join (in the ModifyTable node) to distinguish between the MATCHED and
310  * NOT MATCHED BY SOURCE cases -- see ExecMergeMatched(). Note that this
311  * creates a modified copy of the join condition, for use above the join,
312  * without modifying the original join condition, inside the join.
313  */
314  if (jointype == JOIN_LEFT || jointype == JOIN_FULL)
315  {
316  parse->mergeJoinCondition =
317  add_nulling_relids(parse->mergeJoinCondition,
318  bms_make_singleton(sourcerti),
319  bms_make_singleton(joinrti));
320 
321  foreach_node(MergeAction, action, parse->mergeActionList)
322  {
323  action->qual =
325  bms_make_singleton(sourcerti),
326  bms_make_singleton(joinrti));
327 
328  action->targetList = (List *)
329  add_nulling_relids((Node *) action->targetList,
330  bms_make_singleton(sourcerti),
331  bms_make_singleton(joinrti));
332  }
333 
334  parse->returningList = (List *)
335  add_nulling_relids((Node *) parse->returningList,
336  bms_make_singleton(sourcerti),
337  bms_make_singleton(joinrti));
338  }
339 
340  /*
341  * If there are any WHEN NOT MATCHED BY SOURCE actions, the executor will
342  * use the join condition to distinguish between MATCHED and NOT MATCHED
343  * BY SOURCE cases. Otherwise, it's no longer needed, and we set it to
344  * NULL, saving cycles during planning and execution.
345  *
346  * We need to be careful though: the executor evaluates this condition
347  * using the output of the join subplan node, which nulls the output from
348  * the source relation when the join condition doesn't match. That risks
349  * producing incorrect results when rechecking using a "non-strict" join
350  * condition, such as "src.col IS NOT DISTINCT FROM tgt.col". To guard
351  * against that, we add an additional "src IS NOT NULL" check to the join
352  * condition, so that it does the right thing when performing a recheck
353  * based on the output of the join subplan.
354  */
355  if (have_action[MERGE_WHEN_NOT_MATCHED_BY_SOURCE])
356  {
357  Var *var;
358  NullTest *ntest;
359 
360  /* source wholerow Var (nullable by the new join) */
361  var = makeWholeRowVar(rt_fetch(sourcerti, parse->rtable),
362  sourcerti, 0, false);
363  var->varnullingrels = bms_make_singleton(joinrti);
364 
365  /* "src IS NOT NULL" check */
366  ntest = makeNode(NullTest);
367  ntest->arg = (Expr *) var;
368  ntest->nulltesttype = IS_NOT_NULL;
369  ntest->argisrow = false;
370  ntest->location = -1;
371 
372  /* combine it with the original join condition */
373  parse->mergeJoinCondition =
374  (Node *) make_and_qual((Node *) ntest, parse->mergeJoinCondition);
375  }
376  else
377  parse->mergeJoinCondition = NULL; /* join condition not needed */
378 }
Var * makeWholeRowVar(RangeTblEntry *rte, int varno, Index varlevelsup, bool allowScalar)
Definition: makefuncs.c:135
Node * make_and_qual(Node *qual1, Node *qual2)
Definition: makefuncs.c:730
@ CMD_MERGE
Definition: nodes.h:269
@ CMD_NOTHING
Definition: nodes.h:272
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
static rewind_source * source
Definition: pg_rewind.c:89
#define NUM_MERGE_MATCH_KINDS
Definition: primnodes.h:2001
@ IS_NOT_NULL
Definition: primnodes.h:1952
@ MERGE_WHEN_NOT_MATCHED_BY_TARGET
Definition: primnodes.h:1998
@ MERGE_WHEN_NOT_MATCHED_BY_SOURCE
Definition: primnodes.h:1997
@ MERGE_WHEN_MATCHED
Definition: primnodes.h:1996
Node * quals
Definition: primnodes.h:2289
JoinType jointype
Definition: primnodes.h:2280
int rtindex
Definition: primnodes.h:2293
Node * larg
Definition: primnodes.h:2282
bool isNatural
Definition: primnodes.h:2281
Node * rarg
Definition: primnodes.h:2283
NullTestType nulltesttype
Definition: primnodes.h:1959
ParseLoc location
Definition: primnodes.h:1962
Expr * arg
Definition: primnodes.h:1958

References generate_unaccent_rules::action, add_nulling_relids(), NullTest::arg, Assert, bms_make_singleton(), CMD_MERGE, CMD_NOTHING, elog, ERROR, foreach_node, RangeTblEntry::inh, IS_NOT_NULL, IsA, JoinExpr::isNatural, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, RangeTblEntry::jointype, JoinExpr::jointype, lappend(), JoinExpr::larg, linitial, list_length(), list_make1, NullTest::location, make_and_qual(), makeAlias(), makeFromExpr(), makeNode, makeWholeRowVar(), MERGE_WHEN_MATCHED, MERGE_WHEN_NOT_MATCHED_BY_SOURCE, MERGE_WHEN_NOT_MATCHED_BY_TARGET, NIL, nodeTag, NullTest::nulltesttype, NUM_MERGE_MATCH_KINDS, parse(), JoinExpr::quals, JoinExpr::rarg, rt_fetch, RTE_JOIN, RangeTblEntry::rtekind, RangeTblRef::rtindex, JoinExpr::rtindex, and source.

Referenced by subquery_planner().