PostgreSQL Source Code  git master
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  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 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)
 
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

◆ 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 3601 of file prepjointree.c.

3602 {
3604 
3605  /* If there are no PHVs anywhere, we needn't work hard */
3606  if (root->glob->lastPHId == 0)
3607  return false;
3608 
3609  context.relids = bms_make_singleton(varno);
3610  context.sublevels_up = 0;
3611 
3612  if (query_tree_walker(root->parse,
3614  (void *) &context,
3615  0))
3616  return true;
3617  /* The append_rel_list could be populated already, so check it too */
3620  (void *) &context))
3621  return true;
3622  return false;
3623 }
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:229
#define query_tree_walker(q, w, c, f)
Definition: nodeFuncs.h:156
#define expression_tree_walker(n, w, c)
Definition: nodeFuncs.h:151
static bool find_dependent_phvs_walker(Node *node, find_dependent_phvs_context *context)
Definition: nodes.h:129
Index lastPHId
Definition: pathnodes.h:138
List * append_rel_list
Definition: pathnodes.h:362
PlannerGlobal * glob
Definition: pathnodes.h:202
Query * parse
Definition: pathnodes.h:199

References PlannerInfo::append_rel_list, bms_make_singleton(), expression_tree_walker, find_dependent_phvs_walker(), PlannerInfo::glob, PlannerGlobal::lastPHId, PlannerInfo::parse, query_tree_walker, find_dependent_phvs_context::relids, and find_dependent_phvs_context::sublevels_up.

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 3626 of file prepjointree.c.

3627 {
3629  Relids subrelids;
3630  int relid;
3631 
3632  /* If there are no PHVs anywhere, we needn't work hard */
3633  if (root->glob->lastPHId == 0)
3634  return false;
3635 
3636  context.relids = bms_make_singleton(varno);
3637  context.sublevels_up = 0;
3638 
3639  /*
3640  * See if the jointree fragment itself contains references (in join quals)
3641  */
3642  if (find_dependent_phvs_walker(node, &context))
3643  return true;
3644 
3645  /*
3646  * Otherwise, identify the set of referenced RTEs (we can ignore joins,
3647  * since they should be flattened already, so their join alias lists no
3648  * longer matter), and tediously check each RTE. We can ignore RTEs that
3649  * are not marked LATERAL, though, since they couldn't possibly contain
3650  * any cross-references to other RTEs.
3651  */
3652  subrelids = get_relids_in_jointree(node, false, false);
3653  relid = -1;
3654  while ((relid = bms_next_member(subrelids, relid)) >= 0)
3655  {
3656  RangeTblEntry *rte = rt_fetch(relid, root->parse->rtable);
3657 
3658  if (rte->lateral &&
3661  (void *) &context,
3662  0))
3663  return true;
3664  }
3665 
3666  return false;
3667 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1319
#define range_table_entry_walker(r, w, c, f)
Definition: nodeFuncs.h:166
#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)
List * rtable
Definition: parsenodes.h:167

References bms_make_singleton(), bms_next_member(), find_dependent_phvs_walker(), get_relids_in_jointree(), PlannerInfo::glob, PlannerGlobal::lastPHId, RangeTblEntry::lateral, PlannerInfo::parse, range_table_entry_walker, find_dependent_phvs_context::relids, rt_fetch, Query::rtable, and find_dependent_phvs_context::sublevels_up.

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 3565 of file prepjointree.c.

3567 {
3568  if (node == NULL)
3569  return false;
3570  if (IsA(node, PlaceHolderVar))
3571  {
3572  PlaceHolderVar *phv = (PlaceHolderVar *) node;
3573 
3574  if (phv->phlevelsup == context->sublevels_up &&
3575  bms_equal(context->relids, phv->phrels))
3576  return true;
3577  /* fall through to examine children */
3578  }
3579  if (IsA(node, Query))
3580  {
3581  /* Recurse into subselects */
3582  bool result;
3583 
3584  context->sublevels_up++;
3585  result = query_tree_walker((Query *) node,
3587  (void *) context, 0);
3588  context->sublevels_up--;
3589  return result;
3590  }
3591  /* Shouldn't need to handle most planner auxiliary nodes here */
3592  Assert(!IsA(node, SpecialJoinInfo));
3593  Assert(!IsA(node, PlaceHolderInfo));
3594  Assert(!IsA(node, MinMaxAggInfo));
3595 
3597  (void *) context);
3598 }
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:155
Assert(fmt[strlen(fmt) - 1] !='\n')
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
Index phlevelsup
Definition: pathnodes.h:2774

References Assert(), bms_equal(), expression_tree_walker, IsA, PlaceHolderVar::phlevelsup, query_tree_walker, find_dependent_phvs_context::relids, and find_dependent_phvs_context::sublevels_up.

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 3884 of file prepjointree.c.

3885 {
3886  if (jtnode == NULL)
3887  return NULL;
3888  if (IsA(jtnode, RangeTblRef))
3889  {
3890  int varno = ((RangeTblRef *) jtnode)->rtindex;
3891 
3892  if (relid == varno)
3893  return jtnode;
3894  }
3895  else if (IsA(jtnode, FromExpr))
3896  {
3897  FromExpr *f = (FromExpr *) jtnode;
3898  ListCell *l;
3899 
3900  foreach(l, f->fromlist)
3901  {
3902  jtnode = find_jointree_node_for_rel(lfirst(l), relid);
3903  if (jtnode)
3904  return jtnode;
3905  }
3906  }
3907  else if (IsA(jtnode, JoinExpr))
3908  {
3909  JoinExpr *j = (JoinExpr *) jtnode;
3910 
3911  if (relid == j->rtindex)
3912  return jtnode;
3913  jtnode = find_jointree_node_for_rel(j->larg, relid);
3914  if (jtnode)
3915  return jtnode;
3916  jtnode = find_jointree_node_for_rel(j->rarg, relid);
3917  if (jtnode)
3918  return jtnode;
3919  }
3920  else
3921  elog(ERROR, "unrecognized node type: %d",
3922  (int) nodeTag(jtnode));
3923  return NULL;
3924 }
#define ERROR
Definition: elog.h:39
int j
Definition: isn.c:74
#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:2040

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 3762 of file prepjointree.c.

3763 {
3764  ListCell *l;
3765  int subvarno = -1;
3766 
3767  /*
3768  * We only want to extract the member relid once, but we mustn't fail
3769  * immediately if there are multiple members; it could be that none of the
3770  * AppendRelInfo nodes refer to it. So compute it on first use. Note that
3771  * bms_singleton_member will complain if set is not singleton.
3772  */
3773  foreach(l, root->append_rel_list)
3774  {
3775  AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
3776 
3777  /* The parent_relid shouldn't ever be a pullup target */
3778  Assert(appinfo->parent_relid != varno);
3779 
3780  if (appinfo->child_relid == varno)
3781  {
3782  if (subvarno < 0)
3783  subvarno = bms_singleton_member(subrelids);
3784  appinfo->child_relid = subvarno;
3785  }
3786 
3787  /* Also fix up any PHVs in its translated vars */
3788  if (root->glob->lastPHId != 0)
3790  varno, subrelids);
3791  }
3792 }
int bms_singleton_member(const Bitmapset *a)
Definition: bitmapset.c:685
static void substitute_phv_relids(Node *node, int varno, Relids subrelids)
Index child_relid
Definition: pathnodes.h:2944
List * translated_vars
Definition: pathnodes.h:2971
Index parent_relid
Definition: pathnodes.h:2943

References PlannerInfo::append_rel_list, Assert(), bms_singleton_member(), AppendRelInfo::child_relid, PlannerInfo::glob, PlannerGlobal::lastPHId, lfirst, AppendRelInfo::parent_relid, 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 2539 of file prepjointree.c.

2540 {
2541  Query *parse = root->parse;
2542  SetOperationStmt *topop;
2543  Node *leftmostjtnode;
2544  int leftmostRTI;
2545  RangeTblEntry *leftmostRTE;
2546  int childRTI;
2547  RangeTblEntry *childRTE;
2548  RangeTblRef *rtr;
2549 
2550  /* Shouldn't be called unless query has setops */
2551  topop = castNode(SetOperationStmt, parse->setOperations);
2552  Assert(topop);
2553 
2554  /* Can't optimize away a recursive UNION */
2555  if (root->hasRecursion)
2556  return;
2557 
2558  /*
2559  * Recursively check the tree of set operations. If not all UNION ALL
2560  * with identical column types, punt.
2561  */
2562  if (!is_simple_union_all_recurse((Node *) topop, parse, topop->colTypes))
2563  return;
2564 
2565  /*
2566  * Locate the leftmost leaf query in the setops tree. The upper query's
2567  * Vars all refer to this RTE (see transformSetOperationStmt).
2568  */
2569  leftmostjtnode = topop->larg;
2570  while (leftmostjtnode && IsA(leftmostjtnode, SetOperationStmt))
2571  leftmostjtnode = ((SetOperationStmt *) leftmostjtnode)->larg;
2572  Assert(leftmostjtnode && IsA(leftmostjtnode, RangeTblRef));
2573  leftmostRTI = ((RangeTblRef *) leftmostjtnode)->rtindex;
2574  leftmostRTE = rt_fetch(leftmostRTI, parse->rtable);
2575  Assert(leftmostRTE->rtekind == RTE_SUBQUERY);
2576 
2577  /*
2578  * Make a copy of the leftmost RTE and add it to the rtable. This copy
2579  * will represent the leftmost leaf query in its capacity as a member of
2580  * the appendrel. The original will represent the appendrel as a whole.
2581  * (We must do things this way because the upper query's Vars have to be
2582  * seen as referring to the whole appendrel.)
2583  */
2584  childRTE = copyObject(leftmostRTE);
2585  parse->rtable = lappend(parse->rtable, childRTE);
2586  childRTI = list_length(parse->rtable);
2587 
2588  /* Modify the setops tree to reference the child copy */
2589  ((RangeTblRef *) leftmostjtnode)->rtindex = childRTI;
2590 
2591  /* Modify the formerly-leftmost RTE to mark it as an appendrel parent */
2592  leftmostRTE->inh = true;
2593 
2594  /*
2595  * Form a RangeTblRef for the appendrel, and insert it into FROM. The top
2596  * Query of a setops tree should have had an empty FromClause initially.
2597  */
2598  rtr = makeNode(RangeTblRef);
2599  rtr->rtindex = leftmostRTI;
2600  Assert(parse->jointree->fromlist == NIL);
2601  parse->jointree->fromlist = list_make1(rtr);
2602 
2603  /*
2604  * Now pretend the query has no setops. We must do this before trying to
2605  * do subquery pullup, because of Assert in pull_up_simple_subquery.
2606  */
2607  parse->setOperations = NULL;
2608 
2609  /*
2610  * Build AppendRelInfo information, and apply pull_up_subqueries to the
2611  * leaf queries of the UNION ALL. (We must do that now because they
2612  * weren't previously referenced by the jointree, and so were missed by
2613  * the main invocation of pull_up_subqueries.)
2614  */
2615  pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0);
2616 }
List * lappend(List *list, void *datum)
Definition: list.c:339
#define copyObject(obj)
Definition: nodes.h:223
#define makeNode(_type_)
Definition: nodes.h:155
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
@ RTE_SUBQUERY
Definition: parsenodes.h:1007
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:715
bool hasRecursion
Definition: pathnodes.h:501
RTEKind rtekind
Definition: parsenodes.h:1025

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

Referenced by subquery_planner().

◆ get_relids_for_join()

Relids get_relids_for_join ( Query query,
int  joinrelid 
)

Definition at line 3867 of file prepjointree.c.

3868 {
3869  Node *jtnode;
3870 
3871  jtnode = find_jointree_node_for_rel((Node *) query->jointree,
3872  joinrelid);
3873  if (!jtnode)
3874  elog(ERROR, "could not find join node %d", joinrelid);
3875  return get_relids_in_jointree(jtnode, true, false);
3876 }
FromExpr * jointree
Definition: parsenodes.h:174

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 3806 of file prepjointree.c.

3808 {
3809  Relids result = NULL;
3810 
3811  if (jtnode == NULL)
3812  return result;
3813  if (IsA(jtnode, RangeTblRef))
3814  {
3815  int varno = ((RangeTblRef *) jtnode)->rtindex;
3816 
3817  result = bms_make_singleton(varno);
3818  }
3819  else if (IsA(jtnode, FromExpr))
3820  {
3821  FromExpr *f = (FromExpr *) jtnode;
3822  ListCell *l;
3823 
3824  foreach(l, f->fromlist)
3825  {
3826  result = bms_join(result,
3828  include_outer_joins,
3829  include_inner_joins));
3830  }
3831  }
3832  else if (IsA(jtnode, JoinExpr))
3833  {
3834  JoinExpr *j = (JoinExpr *) jtnode;
3835 
3836  result = get_relids_in_jointree(j->larg,
3837  include_outer_joins,
3838  include_inner_joins);
3839  result = bms_join(result,
3840  get_relids_in_jointree(j->rarg,
3841  include_outer_joins,
3842  include_inner_joins));
3843  if (j->rtindex)
3844  {
3845  if (j->jointype == JOIN_INNER)
3846  {
3847  if (include_inner_joins)
3848  result = bms_add_member(result, j->rtindex);
3849  }
3850  else
3851  {
3852  if (include_outer_joins)
3853  result = bms_add_member(result, j->rtindex);
3854  }
3855  }
3856  }
3857  else
3858  elog(ERROR, "unrecognized node type: %d",
3859  (int) nodeTag(jtnode));
3860  return result;
3861 }
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:1243
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:828
@ JOIN_INNER
Definition: nodes.h:283

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(), 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 3496 of file prepjointree.c.

3497 {
3498  int varno;
3499 
3500  if (!IsA(jtnode, RangeTblRef))
3501  return 0;
3502  varno = ((RangeTblRef *) jtnode)->rtindex;
3503  if (rt_fetch(varno, root->parse->rtable)->rtekind != RTE_RESULT)
3504  return 0;
3505  return varno;
3506 }
@ RTE_RESULT
Definition: parsenodes.h:1014

References IsA, PlannerInfo::parse, rt_fetch, Query::rtable, 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 1962 of file prepjointree.c.

1963 {
1964  FromExpr *jtnode;
1965 
1966  /*
1967  * It's only safe to pull up the child if its jointree contains exactly
1968  * one RTE, else the AppendRelInfo data structure breaks. The one base RTE
1969  * could be buried in several levels of FromExpr, however. Also, if the
1970  * child's jointree is completely empty, we can pull up because
1971  * pull_up_simple_subquery will insert a single RTE_RESULT RTE instead.
1972  *
1973  * Also, the child can't have any WHERE quals because there's no place to
1974  * put them in an appendrel. (This is a bit annoying...) If we didn't
1975  * need to check this, we'd just test whether get_relids_in_jointree()
1976  * yields a singleton set, to be more consistent with the coding of
1977  * fix_append_rel_relids().
1978  */
1979  jtnode = subquery->jointree;
1980  Assert(IsA(jtnode, FromExpr));
1981  /* Check the completely-empty case */
1982  if (jtnode->fromlist == NIL && jtnode->quals == NULL)
1983  return true;
1984  /* Check the more general case */
1985  while (IsA(jtnode, FromExpr))
1986  {
1987  if (jtnode->quals != NULL)
1988  return false;
1989  if (list_length(jtnode->fromlist) != 1)
1990  return false;
1991  jtnode = linitial(jtnode->fromlist);
1992  }
1993  if (!IsA(jtnode, RangeTblRef))
1994  return false;
1995 
1996  return true;
1997 }
#define linitial(l)
Definition: pg_list.h:178
Node * quals
Definition: primnodes.h:2041

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 1487 of file prepjointree.c.

1489 {
1490  /*
1491  * Let's just make sure it's a valid subselect ...
1492  */
1493  if (!IsA(subquery, Query) ||
1494  subquery->commandType != CMD_SELECT)
1495  elog(ERROR, "subquery is bogus");
1496 
1497  /*
1498  * Can't currently pull up a query with setops (unless it's simple UNION
1499  * ALL, which is handled by a different code path). Maybe after querytree
1500  * redesign...
1501  */
1502  if (subquery->setOperations)
1503  return false;
1504 
1505  /*
1506  * Can't pull up a subquery involving grouping, aggregation, SRFs,
1507  * sorting, limiting, or WITH. (XXX WITH could possibly be allowed later)
1508  *
1509  * We also don't pull up a subquery that has explicit FOR UPDATE/SHARE
1510  * clauses, because pullup would cause the locking to occur semantically
1511  * higher than it should. Implicit FOR UPDATE/SHARE is okay because in
1512  * that case the locking was originally declared in the upper query
1513  * anyway.
1514  */
1515  if (subquery->hasAggs ||
1516  subquery->hasWindowFuncs ||
1517  subquery->hasTargetSRFs ||
1518  subquery->groupClause ||
1519  subquery->groupingSets ||
1520  subquery->havingQual ||
1521  subquery->sortClause ||
1522  subquery->distinctClause ||
1523  subquery->limitOffset ||
1524  subquery->limitCount ||
1525  subquery->hasForUpdate ||
1526  subquery->cteList)
1527  return false;
1528 
1529  /*
1530  * Don't pull up if the RTE represents a security-barrier view; we
1531  * couldn't prevent information leakage once the RTE's Vars are scattered
1532  * about in the upper query.
1533  */
1534  if (rte->security_barrier)
1535  return false;
1536 
1537  /*
1538  * If the subquery is LATERAL, check for pullup restrictions from that.
1539  */
1540  if (rte->lateral)
1541  {
1542  bool restricted;
1543  Relids safe_upper_varnos;
1544 
1545  /*
1546  * The subquery's WHERE and JOIN/ON quals mustn't contain any lateral
1547  * references to rels outside a higher outer join (including the case
1548  * where the outer join is within the subquery itself). In such a
1549  * case, pulling up would result in a situation where we need to
1550  * postpone quals from below an outer join to above it, which is
1551  * probably completely wrong and in any case is a complication that
1552  * doesn't seem worth addressing at the moment.
1553  */
1554  if (lowest_outer_join != NULL)
1555  {
1556  restricted = true;
1557  safe_upper_varnos = get_relids_in_jointree((Node *) lowest_outer_join,
1558  true, true);
1559  }
1560  else
1561  {
1562  restricted = false;
1563  safe_upper_varnos = NULL; /* doesn't matter */
1564  }
1565 
1567  (Node *) subquery->jointree,
1568  restricted, safe_upper_varnos))
1569  return false;
1570 
1571  /*
1572  * If there's an outer join above the LATERAL subquery, also disallow
1573  * pullup if the subquery's targetlist has any references to rels
1574  * outside the outer join, since these might get pulled into quals
1575  * above the subquery (but in or below the outer join) and then lead
1576  * to qual-postponement issues similar to the case checked for above.
1577  * (We wouldn't need to prevent pullup if no such references appear in
1578  * outer-query quals, but we don't have enough info here to check
1579  * that. Also, maybe this restriction could be removed if we forced
1580  * such refs to be wrapped in PlaceHolderVars, even when they're below
1581  * the nearest outer join? But it's a pretty hokey usage, so not
1582  * clear this is worth sweating over.)
1583  */
1584  if (lowest_outer_join != NULL)
1585  {
1586  Relids lvarnos = pull_varnos_of_level(root,
1587  (Node *) subquery->targetList,
1588  1);
1589 
1590  if (!bms_is_subset(lvarnos, safe_upper_varnos))
1591  return false;
1592  }
1593  }
1594 
1595  /*
1596  * Don't pull up a subquery that has any volatile functions in its
1597  * targetlist. Otherwise we might introduce multiple evaluations of these
1598  * functions, if they get copied to multiple places in the upper query,
1599  * leading to surprising results. (Note: the PlaceHolderVar mechanism
1600  * doesn't quite guarantee single evaluation; else we could pull up anyway
1601  * and just wrap such items in PlaceHolderVars ...)
1602  */
1603  if (contain_volatile_functions((Node *) subquery->targetList))
1604  return false;
1605 
1606  return true;
1607 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:425
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:521
@ CMD_SELECT
Definition: nodes.h:255
static bool jointree_contains_lateral_outer_refs(PlannerInfo *root, Node *jtnode, bool restricted, Relids safe_upper_varnos)
Node * limitCount
Definition: parsenodes.h:204
Node * setOperations
Definition: parsenodes.h:209
List * cteList
Definition: parsenodes.h:165
List * groupClause
Definition: parsenodes.h:190
Node * havingQual
Definition: parsenodes.h:195
Node * limitOffset
Definition: parsenodes.h:203
CmdType commandType
Definition: parsenodes.h:120
List * targetList
Definition: parsenodes.h:181
List * groupingSets
Definition: parsenodes.h:193
List * distinctClause
Definition: parsenodes.h:199
List * sortClause
Definition: parsenodes.h:201
bool security_barrier
Definition: parsenodes.h:1074
Relids pull_varnos_of_level(PlannerInfo *root, Node *node, int levelsup)
Definition: var.c:134

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(), RangeTblEntry::lateral, Query::limitCount, Query::limitOffset, pull_varnos_of_level(), RangeTblEntry::security_barrier, 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 1891 of file prepjointree.c.

1892 {
1893  SetOperationStmt *topop;
1894 
1895  /* Let's just make sure it's a valid subselect ... */
1896  if (!IsA(subquery, Query) ||
1897  subquery->commandType != CMD_SELECT)
1898  elog(ERROR, "subquery is bogus");
1899 
1900  /* Is it a set-operation query at all? */
1901  topop = castNode(SetOperationStmt, subquery->setOperations);
1902  if (!topop)
1903  return false;
1904 
1905  /* Can't handle ORDER BY, LIMIT/OFFSET, locking, or WITH */
1906  if (subquery->sortClause ||
1907  subquery->limitOffset ||
1908  subquery->limitCount ||
1909  subquery->rowMarks ||
1910  subquery->cteList)
1911  return false;
1912 
1913  /* Recursively check the tree of set operations */
1914  return is_simple_union_all_recurse((Node *) topop, subquery,
1915  topop->colTypes);
1916 }
List * rowMarks
Definition: parsenodes.h:207

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 1919 of file prepjointree.c.

1920 {
1921  /* Since this function recurses, it could be driven to stack overflow. */
1923 
1924  if (IsA(setOp, RangeTblRef))
1925  {
1926  RangeTblRef *rtr = (RangeTblRef *) setOp;
1927  RangeTblEntry *rte = rt_fetch(rtr->rtindex, setOpQuery->rtable);
1928  Query *subquery = rte->subquery;
1929 
1930  Assert(subquery != NULL);
1931 
1932  /* Leaf nodes are OK if they match the toplevel column types */
1933  /* We don't have to compare typmods or collations here */
1934  return tlist_same_datatypes(subquery->targetList, colTypes, true);
1935  }
1936  else if (IsA(setOp, SetOperationStmt))
1937  {
1938  SetOperationStmt *op = (SetOperationStmt *) setOp;
1939 
1940  /* Must be UNION ALL */
1941  if (op->op != SETOP_UNION || !op->all)
1942  return false;
1943 
1944  /* Recurse to check inputs */
1945  return is_simple_union_all_recurse(op->larg, setOpQuery, colTypes) &&
1946  is_simple_union_all_recurse(op->rarg, setOpQuery, colTypes);
1947  }
1948  else
1949  {
1950  elog(ERROR, "unrecognized node type: %d",
1951  (int) nodeTag(setOp));
1952  return false; /* keep compiler quiet */
1953  }
1954 }
@ SETOP_UNION
Definition: parsenodes.h:1949
void check_stack_depth(void)
Definition: postgres.c:3523
Query * subquery
Definition: parsenodes.h:1073
SetOperation op
Definition: parsenodes.h:2026
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 1719 of file prepjointree.c.

1720 {
1721  Assert(rte->rtekind == RTE_VALUES);
1722 
1723  /*
1724  * There must be exactly one VALUES list, else it's not semantically
1725  * correct to replace the VALUES RTE with a RESULT RTE, nor would we have
1726  * a unique set of expressions to substitute into the parent query.
1727  */
1728  if (list_length(rte->values_lists) != 1)
1729  return false;
1730 
1731  /*
1732  * Because VALUES can't appear under an outer join (or at least, we won't
1733  * try to pull it up if it does), we need not worry about LATERAL, nor
1734  * about validity of PHVs for the VALUES' outputs.
1735  */
1736 
1737  /*
1738  * Don't pull up a VALUES that contains any set-returning or volatile
1739  * functions. The considerations here are basically identical to the
1740  * restrictions on a pull-able subquery's targetlist.
1741  */
1742  if (expression_returns_set((Node *) rte->values_lists) ||
1744  return false;
1745 
1746  /*
1747  * Do not pull up a VALUES that's not the only RTE in its parent query.
1748  * This is actually the only case that the parser will generate at the
1749  * moment, and assuming this is true greatly simplifies
1750  * pull_up_simple_values().
1751  */
1752  if (list_length(root->parse->rtable) != 1 ||
1753  rte != (RangeTblEntry *) linitial(root->parse->rtable))
1754  return false;
1755 
1756  return true;
1757 }
bool expression_returns_set(Node *clause)
Definition: nodeFuncs.c:728
@ RTE_VALUES
Definition: parsenodes.h:1011
List * values_lists
Definition: parsenodes.h:1151

References Assert(), contain_volatile_functions(), expression_returns_set(), linitial, list_length(), PlannerInfo::parse, Query::rtable, 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 2010 of file prepjointree.c.

2013 {
2014  if (jtnode == NULL)
2015  return false;
2016  if (IsA(jtnode, RangeTblRef))
2017  return false;
2018  else if (IsA(jtnode, FromExpr))
2019  {
2020  FromExpr *f = (FromExpr *) jtnode;
2021  ListCell *l;
2022 
2023  /* First, recurse to check child joins */
2024  foreach(l, f->fromlist)
2025  {
2027  lfirst(l),
2028  restricted,
2029  safe_upper_varnos))
2030  return true;
2031  }
2032 
2033  /* Then check the top-level quals */
2034  if (restricted &&
2036  safe_upper_varnos))
2037  return true;
2038  }
2039  else if (IsA(jtnode, JoinExpr))
2040  {
2041  JoinExpr *j = (JoinExpr *) jtnode;
2042 
2043  /*
2044  * If this is an outer join, we mustn't allow any upper lateral
2045  * references in or below it.
2046  */
2047  if (j->jointype != JOIN_INNER)
2048  {
2049  restricted = true;
2050  safe_upper_varnos = NULL;
2051  }
2052 
2053  /* Check the child joins */
2055  j->larg,
2056  restricted,
2057  safe_upper_varnos))
2058  return true;
2060  j->rarg,
2061  restricted,
2062  safe_upper_varnos))
2063  return true;
2064 
2065  /* Check the JOIN's qual clauses */
2066  if (restricted &&
2067  !bms_is_subset(pull_varnos_of_level(root, j->quals, 1),
2068  safe_upper_varnos))
2069  return true;
2070  }
2071  else
2072  elog(ERROR, "unrecognized node type: %d",
2073  (int) nodeTag(jtnode));
2074  return false;
2075 }

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

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 1449 of file prepjointree.c.

1451 {
1452  List *vars = NIL;
1453  AttrNumber *pcolnos;
1454  ListCell *l;
1455 
1456  /* Initialize reverse-translation array with all entries zero */
1457  /* (entries for resjunk columns will stay that way) */
1458  appinfo->num_child_cols = list_length(query->targetList);
1459  appinfo->parent_colnos = pcolnos =
1460  (AttrNumber *) palloc0(appinfo->num_child_cols * sizeof(AttrNumber));
1461 
1462  foreach(l, query->targetList)
1463  {
1464  TargetEntry *tle = (TargetEntry *) lfirst(l);
1465 
1466  if (tle->resjunk)
1467  continue;
1468 
1469  vars = lappend(vars, makeVarFromTargetEntry(newvarno, tle));
1470  pcolnos[tle->resno - 1] = tle->resno;
1471  }
1472 
1473  appinfo->translated_vars = vars;
1474 }
int16 AttrNumber
Definition: attnum.h:21
Var * makeVarFromTargetEntry(int varno, TargetEntry *tle)
Definition: makefuncs.c:106
void * palloc0(Size size)
Definition: mcxt.c:1232
int num_child_cols
Definition: pathnodes.h:2979
Definition: pg_list.h:54
AttrNumber resno
Definition: primnodes.h:1924
Definition: regcomp.c:281

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 2085 of file prepjointree.c.

2088 {
2089  Query *parse = root->parse;
2090  ListCell *lc;
2091 
2092  /*
2093  * If we are considering an appendrel child subquery (that is, a UNION ALL
2094  * member query that we're pulling up), then the only part of the upper
2095  * query that could reference the child yet is the translated_vars list of
2096  * the associated AppendRelInfo. Furthermore, we do not want to force use
2097  * of PHVs in the AppendRelInfo --- there isn't any outer join between.
2098  */
2099  if (containing_appendrel)
2100  {
2101  bool save_wrap_non_vars = rvcontext->wrap_non_vars;
2102 
2103  rvcontext->wrap_non_vars = false;
2104  containing_appendrel->translated_vars = (List *)
2105  pullup_replace_vars((Node *) containing_appendrel->translated_vars,
2106  rvcontext);
2107  rvcontext->wrap_non_vars = save_wrap_non_vars;
2108  return;
2109  }
2110 
2111  /*
2112  * Replace all of the top query's references to the subquery's outputs
2113  * with copies of the adjusted subtlist items, being careful not to
2114  * replace any of the jointree structure. (This'd be a lot cleaner if we
2115  * could use query_tree_mutator.) We have to use PHVs in the targetList,
2116  * returningList, and havingQual, since those are certainly above any
2117  * outer join. replace_vars_in_jointree tracks its location in the
2118  * jointree and uses PHVs or not appropriately.
2119  */
2120  parse->targetList = (List *)
2121  pullup_replace_vars((Node *) parse->targetList, rvcontext);
2122  parse->returningList = (List *)
2123  pullup_replace_vars((Node *) parse->returningList, rvcontext);
2124 
2125  foreach(lc, parse->windowClause)
2126  {
2128 
2129  if (wc->runCondition != NIL)
2130  wc->runCondition = (List *)
2131  pullup_replace_vars((Node *) wc->runCondition, rvcontext);
2132  }
2133  if (parse->onConflict)
2134  {
2135  parse->onConflict->onConflictSet = (List *)
2136  pullup_replace_vars((Node *) parse->onConflict->onConflictSet,
2137  rvcontext);
2138  parse->onConflict->onConflictWhere =
2139  pullup_replace_vars(parse->onConflict->onConflictWhere,
2140  rvcontext);
2141 
2142  /*
2143  * We assume ON CONFLICT's arbiterElems, arbiterWhere, exclRelTlist
2144  * can't contain any references to a subquery.
2145  */
2146  }
2147  if (parse->mergeActionList)
2148  {
2149  foreach(lc, parse->mergeActionList)
2150  {
2151  MergeAction *action = lfirst(lc);
2152 
2153  action->qual = pullup_replace_vars(action->qual, rvcontext);
2154  action->targetList = (List *)
2155  pullup_replace_vars((Node *) action->targetList, rvcontext);
2156  }
2157  }
2158  replace_vars_in_jointree((Node *) parse->jointree, rvcontext);
2159  Assert(parse->setOperations == NULL);
2160  parse->havingQual = pullup_replace_vars(parse->havingQual, rvcontext);
2161 
2162  /*
2163  * Replace references in the translated_vars lists of appendrels.
2164  */
2165  foreach(lc, root->append_rel_list)
2166  {
2167  AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
2168 
2169  appinfo->translated_vars = (List *)
2170  pullup_replace_vars((Node *) appinfo->translated_vars, rvcontext);
2171  }
2172 
2173  /*
2174  * Replace references in the joinaliasvars lists of join RTEs.
2175  */
2176  foreach(lc, parse->rtable)
2177  {
2178  RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(lc);
2179 
2180  if (otherrte->rtekind == RTE_JOIN)
2181  otherrte->joinaliasvars = (List *)
2182  pullup_replace_vars((Node *) otherrte->joinaliasvars,
2183  rvcontext);
2184  }
2185 }
@ RTE_JOIN
Definition: parsenodes.h:1008
#define lfirst_node(type, lc)
Definition: pg_list.h:176
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)
List * joinaliasvars
Definition: parsenodes.h:1121

References generate_unaccent_rules::action, PlannerInfo::append_rel_list, Assert(), RangeTblEntry::joinaliasvars, lfirst, lfirst_node, NIL, parse(), PlannerInfo::parse, pullup_replace_vars(), replace_vars_in_jointree(), 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 727 of file prepjointree.c.

728 {
729  ListCell *rt;
730 
731  foreach(rt, root->parse->rtable)
732  {
733  RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
734 
735  if (rte->rtekind == RTE_FUNCTION)
736  {
737  Query *funcquery;
738 
739  /* Apply const-simplification */
740  rte->functions = (List *)
741  eval_const_expressions(root, (Node *) rte->functions);
742 
743  /* Check safety of expansion, and expand if possible */
744  funcquery = inline_set_returning_function(root, rte);
745  if (funcquery)
746  {
747  /* Successful expansion, convert the RTE to a subquery */
748  rte->rtekind = RTE_SUBQUERY;
749  rte->subquery = funcquery;
750  rte->security_barrier = false;
751  /* Clear fields that should not be set in a subquery RTE */
752  rte->functions = NIL;
753  rte->funcordinality = false;
754  }
755  }
756  }
757 }
Query * inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
Definition: clauses.c:5035
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2237
@ RTE_FUNCTION
Definition: parsenodes.h:1009
bool funcordinality
Definition: parsenodes.h:1141
List * functions
Definition: parsenodes.h:1140

References eval_const_expressions(), RangeTblEntry::funcordinality, RangeTblEntry::functions, inline_set_returning_function(), lfirst, NIL, PlannerInfo::parse, Query::rtable, RTE_FUNCTION, RTE_SUBQUERY, RangeTblEntry::rtekind, RangeTblEntry::security_barrier, 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 1778 of file prepjointree.c.

1781 {
1782  Query *parse = root->parse;
1783  RangeTblFunction *rtf;
1784  TypeFuncClass functypclass;
1785  Oid funcrettype;
1786  TupleDesc tupdesc;
1787  pullup_replace_vars_context rvcontext;
1788 
1789  /* Fail if the RTE has ORDINALITY - we don't implement that here. */
1790  if (rte->funcordinality)
1791  return jtnode;
1792 
1793  /* Fail if RTE isn't a single, simple Const expr */
1794  if (list_length(rte->functions) != 1)
1795  return jtnode;
1797  if (!IsA(rtf->funcexpr, Const))
1798  return jtnode;
1799 
1800  /*
1801  * If the function's result is not a scalar, we punt. In principle we
1802  * could break the composite constant value apart into per-column
1803  * constants, but for now it seems not worth the work.
1804  */
1805  if (rtf->funccolcount != 1)
1806  return jtnode; /* definitely composite */
1807 
1808  functypclass = get_expr_result_type(rtf->funcexpr,
1809  &funcrettype,
1810  &tupdesc);
1811  if (functypclass != TYPEFUNC_SCALAR)
1812  return jtnode; /* must be a one-column composite type */
1813 
1814  /* Create context for applying pullup_replace_vars */
1815  rvcontext.root = root;
1816  rvcontext.targetlist = list_make1(makeTargetEntry((Expr *) rtf->funcexpr,
1817  1, /* resno */
1818  NULL, /* resname */
1819  false)); /* resjunk */
1820  rvcontext.target_rte = rte;
1821 
1822  /*
1823  * Since this function was reduced to a Const, it doesn't contain any
1824  * lateral references, even if it's marked as LATERAL. This means we
1825  * don't need to fill relids.
1826  */
1827  rvcontext.relids = NULL;
1828 
1829  rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
1830  rvcontext.varno = ((RangeTblRef *) jtnode)->rtindex;
1831  /* this flag will be set below, if needed */
1832  rvcontext.wrap_non_vars = false;
1833  /* initialize cache array with indexes 0 .. length(tlist) */
1834  rvcontext.rv_cache = palloc0((list_length(rvcontext.targetlist) + 1) *
1835  sizeof(Node *));
1836 
1837  /*
1838  * If we are dealing with an appendrel member then anything that's not a
1839  * simple Var has to be turned into a PlaceHolderVar. (See comments in
1840  * pull_up_simple_subquery().)
1841  */
1842  if (containing_appendrel != NULL)
1843  rvcontext.wrap_non_vars = true;
1844 
1845  /*
1846  * If the parent query uses grouping sets, we need a PlaceHolderVar for
1847  * anything that's not a simple Var.
1848  */
1849  if (parse->groupingSets)
1850  rvcontext.wrap_non_vars = true;
1851 
1852  /*
1853  * Replace all of the top query's references to the RTE's output with
1854  * copies of the funcexpr, being careful not to replace any of the
1855  * jointree structure.
1856  */
1857  perform_pullup_replace_vars(root, &rvcontext,
1858  containing_appendrel);
1859 
1860  /*
1861  * We don't need to bother with changing PlaceHolderVars in the parent
1862  * query. Their references to the RT index are still good for now, and
1863  * will get removed later if we're able to drop the RTE_RESULT.
1864  */
1865 
1866  /*
1867  * Convert the RTE to be RTE_RESULT type, signifying that we don't need to
1868  * scan it anymore, and zero out RTE_FUNCTION-specific fields. Also make
1869  * sure the RTE is not marked LATERAL, since elsewhere we don't expect
1870  * RTE_RESULTs to be LATERAL.
1871  */
1872  rte->rtekind = RTE_RESULT;
1873  rte->functions = NIL;
1874  rte->lateral = false;
1875 
1876  /*
1877  * We can reuse the RangeTblRef node.
1878  */
1879  return jtnode;
1880 }
TypeFuncClass get_expr_result_type(Node *expr, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:292
TypeFuncClass
Definition: funcapi.h:147
@ TYPEFUNC_SCALAR
Definition: funcapi.h:148
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:241
#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)
RangeTblEntry * target_rte
Definition: prepjointree.c:49

References RangeTblFunction::funcexpr, RangeTblEntry::funcordinality, RangeTblEntry::functions, get_expr_result_type(), IsA, RangeTblEntry::lateral, linitial_node, list_length(), list_make1, makeTargetEntry(), NIL, pullup_replace_vars_context::outer_hasSubLinks, palloc0(), parse(), PlannerInfo::parse, perform_pullup_replace_vars(), pullup_replace_vars_context::relids, pullup_replace_vars_context::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 957 of file prepjointree.c.

960 {
961  Query *parse = root->parse;
962  int varno = ((RangeTblRef *) jtnode)->rtindex;
963  Query *subquery;
964  PlannerInfo *subroot;
965  int rtoffset;
966  pullup_replace_vars_context rvcontext;
967  ListCell *lc;
968 
969  /*
970  * Make a modifiable copy of the subquery to hack on, so that the RTE will
971  * be left unchanged in case we decide below that we can't pull it up
972  * after all.
973  */
974  subquery = copyObject(rte->subquery);
975 
976  /*
977  * Create a PlannerInfo data structure for this subquery.
978  *
979  * NOTE: the next few steps should match the first processing in
980  * subquery_planner(). Can we refactor to avoid code duplication, or
981  * would that just make things uglier?
982  */
983  subroot = makeNode(PlannerInfo);
984  subroot->parse = subquery;
985  subroot->glob = root->glob;
986  subroot->query_level = root->query_level;
987  subroot->parent_root = root->parent_root;
988  subroot->plan_params = NIL;
989  subroot->outer_params = NULL;
990  subroot->planner_cxt = CurrentMemoryContext;
991  subroot->init_plans = NIL;
992  subroot->cte_plan_ids = NIL;
993  subroot->multiexpr_params = NIL;
994  subroot->join_domains = NIL;
995  subroot->eq_classes = NIL;
996  subroot->ec_merging_done = false;
997  subroot->last_rinfo_serial = 0;
998  subroot->all_result_relids = NULL;
999  subroot->leaf_result_relids = NULL;
1000  subroot->append_rel_list = NIL;
1001  subroot->row_identity_vars = NIL;
1002  subroot->rowMarks = NIL;
1003  memset(subroot->upper_rels, 0, sizeof(subroot->upper_rels));
1004  memset(subroot->upper_targets, 0, sizeof(subroot->upper_targets));
1005  subroot->processed_groupClause = NIL;
1006  subroot->processed_distinctClause = NIL;
1007  subroot->processed_tlist = NIL;
1008  subroot->update_colnos = NIL;
1009  subroot->grouping_map = NULL;
1010  subroot->minmax_aggs = NIL;
1011  subroot->qual_security_level = 0;
1012  subroot->placeholdersFrozen = false;
1013  subroot->hasRecursion = false;
1014  subroot->wt_param_id = -1;
1015  subroot->non_recursive_path = NULL;
1016  /* We don't currently need a top JoinDomain for the subroot */
1017 
1018  /* No CTEs to worry about */
1019  Assert(subquery->cteList == NIL);
1020 
1021  /*
1022  * If the FROM clause is empty, replace it with a dummy RTE_RESULT RTE, so
1023  * that we don't need so many special cases to deal with that situation.
1024  */
1025  replace_empty_jointree(subquery);
1026 
1027  /*
1028  * Pull up any SubLinks within the subquery's quals, so that we don't
1029  * leave unoptimized SubLinks behind.
1030  */
1031  if (subquery->hasSubLinks)
1032  pull_up_sublinks(subroot);
1033 
1034  /*
1035  * Similarly, preprocess its function RTEs to inline any set-returning
1036  * functions in its rangetable.
1037  */
1038  preprocess_function_rtes(subroot);
1039 
1040  /*
1041  * Recursively pull up the subquery's subqueries, so that
1042  * pull_up_subqueries' processing is complete for its jointree and
1043  * rangetable.
1044  *
1045  * Note: it's okay that the subquery's recursion starts with NULL for
1046  * containing-join info, even if we are within an outer join in the upper
1047  * query; the lower query starts with a clean slate for outer-join
1048  * semantics. Likewise, we needn't pass down appendrel state.
1049  */
1050  pull_up_subqueries(subroot);
1051 
1052  /*
1053  * Now we must recheck whether the subquery is still simple enough to pull
1054  * up. If not, abandon processing it.
1055  *
1056  * We don't really need to recheck all the conditions involved, but it's
1057  * easier just to keep this "if" looking the same as the one in
1058  * pull_up_subqueries_recurse.
1059  */
1060  if (is_simple_subquery(root, subquery, rte, lowest_outer_join) &&
1061  (containing_appendrel == NULL || is_safe_append_member(subquery)))
1062  {
1063  /* good to go */
1064  }
1065  else
1066  {
1067  /*
1068  * Give up, return unmodified RangeTblRef.
1069  *
1070  * Note: The work we just did will be redone when the subquery gets
1071  * planned on its own. Perhaps we could avoid that by storing the
1072  * modified subquery back into the rangetable, but I'm not gonna risk
1073  * it now.
1074  */
1075  return jtnode;
1076  }
1077 
1078  /*
1079  * We must flatten any join alias Vars in the subquery's targetlist,
1080  * because pulling up the subquery's subqueries might have changed their
1081  * expansions into arbitrary expressions, which could affect
1082  * pullup_replace_vars' decisions about whether PlaceHolderVar wrappers
1083  * are needed for tlist entries. (Likely it'd be better to do
1084  * flatten_join_alias_vars on the whole query tree at some earlier stage,
1085  * maybe even in the rewriter; but for now let's just fix this case here.)
1086  */
1087  subquery->targetList = (List *)
1088  flatten_join_alias_vars(subroot, subroot->parse,
1089  (Node *) subquery->targetList);
1090 
1091  /*
1092  * Adjust level-0 varnos in subquery so that we can append its rangetable
1093  * to upper query's. We have to fix the subquery's append_rel_list as
1094  * well.
1095  */
1096  rtoffset = list_length(parse->rtable);
1097  OffsetVarNodes((Node *) subquery, rtoffset, 0);
1098  OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);
1099 
1100  /*
1101  * Upper-level vars in subquery are now one level closer to their parent
1102  * than before.
1103  */
1104  IncrementVarSublevelsUp((Node *) subquery, -1, 1);
1105  IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
1106 
1107  /*
1108  * The subquery's targetlist items are now in the appropriate form to
1109  * insert into the top query, except that we may need to wrap them in
1110  * PlaceHolderVars. Set up required context data for pullup_replace_vars.
1111  * (Note that we should include the subquery's inner joins in relids,
1112  * since it may include join alias vars referencing them.)
1113  */
1114  rvcontext.root = root;
1115  rvcontext.targetlist = subquery->targetList;
1116  rvcontext.target_rte = rte;
1117  if (rte->lateral)
1118  rvcontext.relids = get_relids_in_jointree((Node *) subquery->jointree,
1119  true, true);
1120  else /* won't need relids */
1121  rvcontext.relids = NULL;
1122  rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
1123  rvcontext.varno = varno;
1124  /* this flag will be set below, if needed */
1125  rvcontext.wrap_non_vars = false;
1126  /* initialize cache array with indexes 0 .. length(tlist) */
1127  rvcontext.rv_cache = palloc0((list_length(subquery->targetList) + 1) *
1128  sizeof(Node *));
1129 
1130  /*
1131  * If we are dealing with an appendrel member then anything that's not a
1132  * simple Var has to be turned into a PlaceHolderVar. We force this to
1133  * ensure that what we pull up doesn't get merged into a surrounding
1134  * expression during later processing and then fail to match the
1135  * expression actually available from the appendrel.
1136  */
1137  if (containing_appendrel != NULL)
1138  rvcontext.wrap_non_vars = true;
1139 
1140  /*
1141  * If the parent query uses grouping sets, we need a PlaceHolderVar for
1142  * anything that's not a simple Var. Again, this ensures that expressions
1143  * retain their separate identity so that they will match grouping set
1144  * columns when appropriate. (It'd be sufficient to wrap values used in
1145  * grouping set columns, and do so only in non-aggregated portions of the
1146  * tlist and havingQual, but that would require a lot of infrastructure
1147  * that pullup_replace_vars hasn't currently got.)
1148  */
1149  if (parse->groupingSets)
1150  rvcontext.wrap_non_vars = true;
1151 
1152  /*
1153  * Replace all of the top query's references to the subquery's outputs
1154  * with copies of the adjusted subtlist items, being careful not to
1155  * replace any of the jointree structure.
1156  */
1157  perform_pullup_replace_vars(root, &rvcontext,
1158  containing_appendrel);
1159 
1160  /*
1161  * If the subquery had a LATERAL marker, propagate that to any of its
1162  * child RTEs that could possibly now contain lateral cross-references.
1163  * The children might or might not contain any actual lateral
1164  * cross-references, but we have to mark the pulled-up child RTEs so that
1165  * later planner stages will check for such.
1166  */
1167  if (rte->lateral)
1168  {
1169  foreach(lc, subquery->rtable)
1170  {
1171  RangeTblEntry *child_rte = (RangeTblEntry *) lfirst(lc);
1172 
1173  switch (child_rte->rtekind)
1174  {
1175  case RTE_RELATION:
1176  if (child_rte->tablesample)
1177  child_rte->lateral = true;
1178  break;
1179  case RTE_SUBQUERY:
1180  case RTE_FUNCTION:
1181  case RTE_VALUES:
1182  case RTE_TABLEFUNC:
1183  child_rte->lateral = true;
1184  break;
1185  case RTE_JOIN:
1186  case RTE_CTE:
1187  case RTE_NAMEDTUPLESTORE:
1188  case RTE_RESULT:
1189  /* these can't contain any lateral references */
1190  break;
1191  }
1192  }
1193  }
1194 
1195  /*
1196  * Now append the adjusted rtable entries and their perminfos to upper
1197  * query. (We hold off until after fixing the upper rtable entries; no
1198  * point in running that code on the subquery ones too.)
1199  */
1200  CombineRangeTables(&parse->rtable, &parse->rteperminfos,
1201  subquery->rtable, subquery->rteperminfos);
1202 
1203  /*
1204  * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already
1205  * adjusted the marker rtindexes, so just concat the lists.)
1206  */
1207  parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
1208 
1209  /*
1210  * We also have to fix the relid sets of any PlaceHolderVar nodes in the
1211  * parent query. (This could perhaps be done by pullup_replace_vars(),
1212  * but it seems cleaner to use two passes.) Note in particular that any
1213  * PlaceHolderVar nodes just created by pullup_replace_vars() will be
1214  * adjusted, so having created them with the subquery's varno is correct.
1215  *
1216  * Likewise, relids appearing in AppendRelInfo nodes have to be fixed. We
1217  * already checked that this won't require introducing multiple subrelids
1218  * into the single-slot AppendRelInfo structs.
1219  */
1220  if (root->glob->lastPHId != 0 || root->append_rel_list)
1221  {
1222  Relids subrelids;
1223 
1224  subrelids = get_relids_in_jointree((Node *) subquery->jointree,
1225  true, false);
1226  if (root->glob->lastPHId != 0)
1227  substitute_phv_relids((Node *) parse, varno, subrelids);
1228  fix_append_rel_relids(root, varno, subrelids);
1229  }
1230 
1231  /*
1232  * And now add subquery's AppendRelInfos to our list.
1233  */
1235  subroot->append_rel_list);
1236 
1237  /*
1238  * We don't have to do the equivalent bookkeeping for outer-join info,
1239  * because that hasn't been set up yet. placeholder_list likewise.
1240  */
1241  Assert(root->join_info_list == NIL);
1242  Assert(subroot->join_info_list == NIL);
1243  Assert(root->placeholder_list == NIL);
1244  Assert(subroot->placeholder_list == NIL);
1245 
1246  /*
1247  * We no longer need the RTE's copy of the subquery's query tree. Getting
1248  * rid of it saves nothing in particular so far as this level of query is
1249  * concerned; but if this query level is in turn pulled up into a parent,
1250  * we'd waste cycles copying the now-unused query tree.
1251  */
1252  rte->subquery = NULL;
1253 
1254  /*
1255  * Miscellaneous housekeeping.
1256  *
1257  * Although replace_rte_variables() faithfully updated parse->hasSubLinks
1258  * if it copied any SubLinks out of the subquery's targetlist, we still
1259  * could have SubLinks added to the query in the expressions of FUNCTION
1260  * and VALUES RTEs copied up from the subquery. So it's necessary to copy
1261  * subquery->hasSubLinks anyway. Perhaps this can be improved someday.
1262  */
1263  parse->hasSubLinks |= subquery->hasSubLinks;
1264 
1265  /* If subquery had any RLS conditions, now main query does too */
1266  parse->hasRowSecurity |= subquery->hasRowSecurity;
1267 
1268  /*
1269  * subquery won't be pulled up if it hasAggs, hasWindowFuncs, or
1270  * hasTargetSRFs, so no work needed on those flags
1271  */
1272 
1273  /*
1274  * Return the adjusted subquery jointree to replace the RangeTblRef entry
1275  * in parent's jointree; or, if the FromExpr is degenerate, just return
1276  * its single member.
1277  */
1278  Assert(IsA(subquery->jointree, FromExpr));
1279  Assert(subquery->jointree->fromlist != NIL);
1280  if (subquery->jointree->quals == NULL &&
1281  list_length(subquery->jointree->fromlist) == 1)
1282  return (Node *) linitial(subquery->jointree->fromlist);
1283 
1284  return (Node *) subquery->jointree;
1285 }
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
@ RTE_CTE
Definition: parsenodes.h:1012
@ RTE_NAMEDTUPLESTORE
Definition: parsenodes.h:1013
@ RTE_TABLEFUNC
Definition: parsenodes.h:1010
@ RTE_RELATION
Definition: parsenodes.h:1006
void preprocess_function_rtes(PlannerInfo *root)
Definition: prepjointree.c:727
static bool is_simple_subquery(PlannerInfo *root, Query *subquery, RangeTblEntry *rte, JoinExpr *lowest_outer_join)
void pull_up_sublinks(PlannerInfo *root)
Definition: prepjointree.c:293
void replace_empty_jointree(Query *parse)
Definition: prepjointree.c:235
static void fix_append_rel_relids(PlannerInfo *root, int varno, Relids subrelids)
void pull_up_subqueries(PlannerInfo *root)
Definition: prepjointree.c:768
static bool is_safe_append_member(Query *subquery)
void OffsetVarNodes(Node *node, int offset, int sublevels_up)
Definition: rewriteManip.c:480
void CombineRangeTables(List **dst_rtable, List **dst_perminfos, List *src_rtable, List *src_perminfos)
Definition: rewriteManip.c:350
void IncrementVarSublevelsUp(Node *node, int delta_sublevels_up, int min_sublevels_up)
Definition: rewriteManip.c:841
List * minmax_aggs
Definition: pathnodes.h:469
List * processed_tlist
Definition: pathnodes.h:453
List * cte_plan_ids
Definition: pathnodes.h:302
int last_rinfo_serial
Definition: pathnodes.h:340
Index qual_security_level
Definition: pathnodes.h:486
List * init_plans
Definition: pathnodes.h:296
List * multiexpr_params
Definition: pathnodes.h:305
List * row_identity_vars
Definition: pathnodes.h:365
bool ec_merging_done
Definition: pathnodes.h:314
Bitmapset * outer_params
Definition: pathnodes.h:218
Index query_level
Definition: pathnodes.h:205
struct Path * non_recursive_path
Definition: pathnodes.h:523
List * placeholder_list
Definition: pathnodes.h:371
List * join_domains
Definition: pathnodes.h:308
List * eq_classes
Definition: pathnodes.h:311
int wt_param_id
Definition: pathnodes.h:521
List * plan_params
Definition: pathnodes.h:217
List * processed_groupClause
Definition: pathnodes.h:430
List * processed_distinctClause
Definition: pathnodes.h:442
List * rowMarks
Definition: pathnodes.h:368
List * update_colnos
Definition: pathnodes.h:461
bool placeholdersFrozen
Definition: pathnodes.h:499
List * join_info_list
Definition: pathnodes.h:337
Relids all_result_relids
Definition: pathnodes.h:351
Relids leaf_result_relids
Definition: pathnodes.h:353
struct TableSampleClause * tablesample
Definition: parsenodes.h:1067
Node * flatten_join_alias_vars(PlannerInfo *root, Query *query, Node *node)
Definition: var.c:744

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_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, PlannerGlobal::lastPHId, RangeTblEntry::lateral, PlannerInfo::leaf_result_relids, lfirst, linitial, list_concat(), list_length(), makeNode, PlannerInfo::minmax_aggs, PlannerInfo::multiexpr_params, NIL, PlannerInfo::non_recursive_path, 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, PlannerInfo::row_identity_vars, Query::rowMarks, PlannerInfo::rowMarks, Query::rtable, RTE_CTE, RTE_FUNCTION, 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 1297 of file prepjointree.c.

1298 {
1299  int varno = ((RangeTblRef *) jtnode)->rtindex;
1300  Query *subquery = rte->subquery;
1301  int rtoffset = list_length(root->parse->rtable);
1302  List *rtable;
1303 
1304  /*
1305  * Make a modifiable copy of the subquery's rtable, so we can adjust
1306  * upper-level Vars in it. There are no such Vars in the setOperations
1307  * tree proper, so fixing the rtable should be sufficient.
1308  */
1309  rtable = copyObject(subquery->rtable);
1310 
1311  /*
1312  * Upper-level vars in subquery are now one level closer to their parent
1313  * than before. We don't have to worry about offsetting varnos, though,
1314  * because the UNION leaf queries can't cross-reference each other.
1315  */
1316  IncrementVarSublevelsUp_rtable(rtable, -1, 1);
1317 
1318  /*
1319  * If the UNION ALL subquery had a LATERAL marker, propagate that to all
1320  * its children. The individual children might or might not contain any
1321  * actual lateral cross-references, but we have to mark the pulled-up
1322  * child RTEs so that later planner stages will check for such.
1323  */
1324  if (rte->lateral)
1325  {
1326  ListCell *rt;
1327 
1328  foreach(rt, rtable)
1329  {
1330  RangeTblEntry *child_rte = (RangeTblEntry *) lfirst(rt);
1331 
1332  Assert(child_rte->rtekind == RTE_SUBQUERY);
1333  child_rte->lateral = true;
1334  }
1335  }
1336 
1337  /*
1338  * Append child RTEs (and their perminfos) to parent rtable.
1339  */
1340  CombineRangeTables(&root->parse->rtable, &root->parse->rteperminfos,
1341  rtable, subquery->rteperminfos);
1342 
1343  /*
1344  * Recursively scan the subquery's setOperations tree and add
1345  * AppendRelInfo nodes for leaf subqueries to the parent's
1346  * append_rel_list. Also apply pull_up_subqueries to the leaf subqueries.
1347  */
1348  Assert(subquery->setOperations);
1349  pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery,
1350  rtoffset);
1351 
1352  /*
1353  * Mark the parent as an append relation.
1354  */
1355  rte->inh = true;
1356 
1357  return jtnode;
1358 }
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, RangeTblEntry::lateral, lfirst, list_length(), PlannerInfo::parse, pull_up_union_leaf_queries(), 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 1624 of file prepjointree.c.

1625 {
1626  Query *parse = root->parse;
1627  int varno = ((RangeTblRef *) jtnode)->rtindex;
1628  List *values_list;
1629  List *tlist;
1630  AttrNumber attrno;
1631  pullup_replace_vars_context rvcontext;
1632  ListCell *lc;
1633 
1634  Assert(rte->rtekind == RTE_VALUES);
1635  Assert(list_length(rte->values_lists) == 1);
1636 
1637  /*
1638  * Need a modifiable copy of the VALUES list to hack on, just in case it's
1639  * multiply referenced.
1640  */
1641  values_list = copyObject(linitial(rte->values_lists));
1642 
1643  /*
1644  * The VALUES RTE can't contain any Vars of level zero, let alone any that
1645  * are join aliases, so no need to flatten join alias Vars.
1646  */
1647  Assert(!contain_vars_of_level((Node *) values_list, 0));
1648 
1649  /*
1650  * Set up required context data for pullup_replace_vars. In particular,
1651  * we have to make the VALUES list look like a subquery targetlist.
1652  */
1653  tlist = NIL;
1654  attrno = 1;
1655  foreach(lc, values_list)
1656  {
1657  tlist = lappend(tlist,
1658  makeTargetEntry((Expr *) lfirst(lc),
1659  attrno,
1660  NULL,
1661  false));
1662  attrno++;
1663  }
1664  rvcontext.root = root;
1665  rvcontext.targetlist = tlist;
1666  rvcontext.target_rte = rte;
1667  rvcontext.relids = NULL;
1668  rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
1669  rvcontext.varno = varno;
1670  rvcontext.wrap_non_vars = false;
1671  /* initialize cache array with indexes 0 .. length(tlist) */
1672  rvcontext.rv_cache = palloc0((list_length(tlist) + 1) *
1673  sizeof(Node *));
1674 
1675  /*
1676  * Replace all of the top query's references to the RTE's outputs with
1677  * copies of the adjusted VALUES expressions, being careful not to replace
1678  * any of the jointree structure. We can assume there's no outer joins or
1679  * appendrels in the dummy Query that surrounds a VALUES RTE.
1680  */
1681  perform_pullup_replace_vars(root, &rvcontext, NULL);
1682 
1683  /*
1684  * There should be no appendrels to fix, nor any outer joins and hence no
1685  * PlaceHolderVars.
1686  */
1687  Assert(root->append_rel_list == NIL);
1688  Assert(root->join_info_list == NIL);
1689  Assert(root->placeholder_list == NIL);
1690 
1691  /*
1692  * Replace the VALUES RTE with a RESULT RTE. The VALUES RTE is the only
1693  * rtable entry in the current query level, so this is easy.
1694  */
1695  Assert(list_length(parse->rtable) == 1);
1696 
1697  /* Create suitable RTE */
1698  rte = makeNode(RangeTblEntry);
1699  rte->rtekind = RTE_RESULT;
1700  rte->eref = makeAlias("*RESULT*", NIL);
1701 
1702  /* Replace rangetable */
1703  parse->rtable = list_make1(rte);
1704 
1705  /* We could manufacture a new RangeTblRef, but the one we have is fine */
1706  Assert(varno == 1);
1707 
1708  return jtnode;
1709 }
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:390
Alias * eref
Definition: parsenodes.h:1192
bool contain_vars_of_level(Node *node, int levelsup)
Definition: var.c:441

References PlannerInfo::append_rel_list, Assert(), contain_vars_of_level(), copyObject, RangeTblEntry::eref, PlannerInfo::join_info_list, lappend(), lfirst, linitial, list_length(), list_make1, makeAlias(), makeNode, makeTargetEntry(), NIL, pullup_replace_vars_context::outer_hasSubLinks, palloc0(), parse(), PlannerInfo::parse, perform_pullup_replace_vars(), PlannerInfo::placeholder_list, pullup_replace_vars_context::relids, pullup_replace_vars_context::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 293 of file prepjointree.c.

294 {
295  Node *jtnode;
296  Relids relids;
297 
298  /* Begin recursion through the jointree */
299  jtnode = pull_up_sublinks_jointree_recurse(root,
300  (Node *) root->parse->jointree,
301  &relids);
302 
303  /*
304  * root->parse->jointree must always be a FromExpr, so insert a dummy one
305  * if we got a bare RangeTblRef or JoinExpr out of the recursion.
306  */
307  if (IsA(jtnode, FromExpr))
308  root->parse->jointree = (FromExpr *) jtnode;
309  else
310  root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL);
311 }
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:288
static Node * pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, Relids *relids)
Definition: prepjointree.c:320

References IsA, Query::jointree, list_make1, makeFromExpr(), PlannerInfo::parse, and pull_up_sublinks_jointree_recurse().

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 320 of file prepjointree.c.

322 {
323  /* Since this function recurses, it could be driven to stack overflow. */
325 
326  if (jtnode == NULL)
327  {
328  *relids = NULL;
329  }
330  else if (IsA(jtnode, RangeTblRef))
331  {
332  int varno = ((RangeTblRef *) jtnode)->rtindex;
333 
334  *relids = bms_make_singleton(varno);
335  /* jtnode is returned unmodified */
336  }
337  else if (IsA(jtnode, FromExpr))
338  {
339  FromExpr *f = (FromExpr *) jtnode;
340  List *newfromlist = NIL;
341  Relids frelids = NULL;
342  FromExpr *newf;
343  Node *jtlink;
344  ListCell *l;
345 
346  /* First, recurse to process children and collect their relids */
347  foreach(l, f->fromlist)
348  {
349  Node *newchild;
350  Relids childrelids;
351 
352  newchild = pull_up_sublinks_jointree_recurse(root,
353  lfirst(l),
354  &childrelids);
355  newfromlist = lappend(newfromlist, newchild);
356  frelids = bms_join(frelids, childrelids);
357  }
358  /* Build the replacement FromExpr; no quals yet */
359  newf = makeFromExpr(newfromlist, NULL);
360  /* Set up a link representing the rebuilt jointree */
361  jtlink = (Node *) newf;
362  /* Now process qual --- all children are available for use */
363  newf->quals = pull_up_sublinks_qual_recurse(root, f->quals,
364  &jtlink, frelids,
365  NULL, NULL);
366 
367  /*
368  * Note that the result will be either newf, or a stack of JoinExprs
369  * with newf at the base. We rely on subsequent optimization steps to
370  * flatten this and rearrange the joins as needed.
371  *
372  * Although we could include the pulled-up subqueries in the returned
373  * relids, there's no need since upper quals couldn't refer to their
374  * outputs anyway.
375  */
376  *relids = frelids;
377  jtnode = jtlink;
378  }
379  else if (IsA(jtnode, JoinExpr))
380  {
381  JoinExpr *j;
382  Relids leftrelids;
383  Relids rightrelids;
384  Node *jtlink;
385 
386  /*
387  * Make a modifiable copy of join node, but don't bother copying its
388  * subnodes (yet).
389  */
390  j = (JoinExpr *) palloc(sizeof(JoinExpr));
391  memcpy(j, jtnode, sizeof(JoinExpr));
392  jtlink = (Node *) j;
393 
394  /* Recurse to process children and collect their relids */
395  j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,
396  &leftrelids);
397  j->rarg = pull_up_sublinks_jointree_recurse(root, j->rarg,
398  &rightrelids);
399 
400  /*
401  * Now process qual, showing appropriate child relids as available,
402  * and attach any pulled-up jointree items at the right place. In the
403  * inner-join case we put new JoinExprs above the existing one (much
404  * as for a FromExpr-style join). In outer-join cases the new
405  * JoinExprs must go into the nullable side of the outer join. The
406  * point of the available_rels machinations is to ensure that we only
407  * pull up quals for which that's okay.
408  *
409  * We don't expect to see any pre-existing JOIN_SEMI, JOIN_ANTI, or
410  * JOIN_RIGHT_ANTI jointypes here.
411  */
412  switch (j->jointype)
413  {
414  case JOIN_INNER:
415  j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
416  &jtlink,
417  bms_union(leftrelids,
418  rightrelids),
419  NULL, NULL);
420  break;
421  case JOIN_LEFT:
422  j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
423  &j->rarg,
424  rightrelids,
425  NULL, NULL);
426  break;
427  case JOIN_FULL:
428  /* can't do anything with full-join quals */
429  break;
430  case JOIN_RIGHT:
431  j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
432  &j->larg,
433  leftrelids,
434  NULL, NULL);
435  break;
436  default:
437  elog(ERROR, "unrecognized join type: %d",
438  (int) j->jointype);
439  break;
440  }
441 
442  /*
443  * Although we could include the pulled-up subqueries in the returned
444  * relids, there's no need since upper quals couldn't refer to their
445  * outputs anyway. But we *do* need to include the join's own rtindex
446  * because we haven't yet collapsed join alias variables, so upper
447  * levels would mistakenly think they couldn't use references to this
448  * join.
449  */
450  *relids = bms_join(leftrelids, rightrelids);
451  if (j->rtindex)
452  *relids = bms_add_member(*relids, j->rtindex);
453  jtnode = jtlink;
454  }
455  else
456  elog(ERROR, "unrecognized node type: %d",
457  (int) nodeTag(jtnode));
458  return jtnode;
459 }
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:264
void * palloc(Size size)
Definition: mcxt.c:1201
@ JOIN_FULL
Definition: nodes.h:285
@ JOIN_RIGHT
Definition: nodes.h:286
@ JOIN_LEFT
Definition: nodes.h:284
static Node * pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, Node **jtlink1, Relids available_rels1, Node **jtlink2, Relids available_rels2)
Definition: prepjointree.c:477

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(), and FromExpr::quals.

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 477 of file prepjointree.c.

480 {
481  if (node == NULL)
482  return NULL;
483  if (IsA(node, SubLink))
484  {
485  SubLink *sublink = (SubLink *) node;
486  JoinExpr *j;
487  Relids child_rels;
488 
489  /* Is it a convertible ANY or EXISTS clause? */
490  if (sublink->subLinkType == ANY_SUBLINK)
491  {
492  if ((j = convert_ANY_sublink_to_join(root, sublink,
493  available_rels1)) != NULL)
494  {
495  /* Yes; insert the new join node into the join tree */
496  j->larg = *jtlink1;
497  *jtlink1 = (Node *) j;
498  /* Recursively process pulled-up jointree nodes */
500  j->rarg,
501  &child_rels);
502 
503  /*
504  * Now recursively process the pulled-up quals. Any inserted
505  * joins can get stacked onto either j->larg or j->rarg,
506  * depending on which rels they reference.
507  */
508  j->quals = pull_up_sublinks_qual_recurse(root,
509  j->quals,
510  &j->larg,
511  available_rels1,
512  &j->rarg,
513  child_rels);
514  /* Return NULL representing constant TRUE */
515  return NULL;
516  }
517  if (available_rels2 != NULL &&
518  (j = convert_ANY_sublink_to_join(root, sublink,
519  available_rels2)) != NULL)
520  {
521  /* Yes; insert the new join node into the join tree */
522  j->larg = *jtlink2;
523  *jtlink2 = (Node *) j;
524  /* Recursively process pulled-up jointree nodes */
526  j->rarg,
527  &child_rels);
528 
529  /*
530  * Now recursively process the pulled-up quals. Any inserted
531  * joins can get stacked onto either j->larg or j->rarg,
532  * depending on which rels they reference.
533  */
534  j->quals = pull_up_sublinks_qual_recurse(root,
535  j->quals,
536  &j->larg,
537  available_rels2,
538  &j->rarg,
539  child_rels);
540  /* Return NULL representing constant TRUE */
541  return NULL;
542  }
543  }
544  else if (sublink->subLinkType == EXISTS_SUBLINK)
545  {
546  if ((j = convert_EXISTS_sublink_to_join(root, sublink, false,
547  available_rels1)) != NULL)
548  {
549  /* Yes; insert the new join node into the join tree */
550  j->larg = *jtlink1;
551  *jtlink1 = (Node *) j;
552  /* Recursively process pulled-up jointree nodes */
554  j->rarg,
555  &child_rels);
556 
557  /*
558  * Now recursively process the pulled-up quals. Any inserted
559  * joins can get stacked onto either j->larg or j->rarg,
560  * depending on which rels they reference.
561  */
562  j->quals = pull_up_sublinks_qual_recurse(root,
563  j->quals,
564  &j->larg,
565  available_rels1,
566  &j->rarg,
567  child_rels);
568  /* Return NULL representing constant TRUE */
569  return NULL;
570  }
571  if (available_rels2 != NULL &&
572  (j = convert_EXISTS_sublink_to_join(root, sublink, false,
573  available_rels2)) != NULL)
574  {
575  /* Yes; insert the new join node into the join tree */
576  j->larg = *jtlink2;
577  *jtlink2 = (Node *) j;
578  /* Recursively process pulled-up jointree nodes */
580  j->rarg,
581  &child_rels);
582 
583  /*
584  * Now recursively process the pulled-up quals. Any inserted
585  * joins can get stacked onto either j->larg or j->rarg,
586  * depending on which rels they reference.
587  */
588  j->quals = pull_up_sublinks_qual_recurse(root,
589  j->quals,
590  &j->larg,
591  available_rels2,
592  &j->rarg,
593  child_rels);
594  /* Return NULL representing constant TRUE */
595  return NULL;
596  }
597  }
598  /* Else return it unmodified */
599  return node;
600  }
601  if (is_notclause(node))
602  {
603  /* If the immediate argument of NOT is EXISTS, try to convert */
604  SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node);
605  JoinExpr *j;
606  Relids child_rels;
607 
608  if (sublink && IsA(sublink, SubLink))
609  {
610  if (sublink->subLinkType == EXISTS_SUBLINK)
611  {
612  if ((j = convert_EXISTS_sublink_to_join(root, sublink, true,
613  available_rels1)) != NULL)
614  {
615  /* Yes; insert the new join node into the join tree */
616  j->larg = *jtlink1;
617  *jtlink1 = (Node *) j;
618  /* Recursively process pulled-up jointree nodes */
620  j->rarg,
621  &child_rels);
622 
623  /*
624  * Now recursively process the pulled-up quals. Because
625  * we are underneath a NOT, we can't pull up sublinks that
626  * reference the left-hand stuff, but it's still okay to
627  * pull up sublinks referencing j->rarg.
628  */
629  j->quals = pull_up_sublinks_qual_recurse(root,
630  j->quals,
631  &j->rarg,
632  child_rels,
633  NULL, NULL);
634  /* Return NULL representing constant TRUE */
635  return NULL;
636  }
637  if (available_rels2 != NULL &&
638  (j = convert_EXISTS_sublink_to_join(root, sublink, true,
639  available_rels2)) != NULL)
640  {
641  /* Yes; insert the new join node into the join tree */
642  j->larg = *jtlink2;
643  *jtlink2 = (Node *) j;
644  /* Recursively process pulled-up jointree nodes */
646  j->rarg,
647  &child_rels);
648 
649  /*
650  * Now recursively process the pulled-up quals. Because
651  * we are underneath a NOT, we can't pull up sublinks that
652  * reference the left-hand stuff, but it's still okay to
653  * pull up sublinks referencing j->rarg.
654  */
655  j->quals = pull_up_sublinks_qual_recurse(root,
656  j->quals,
657  &j->rarg,
658  child_rels,
659  NULL, NULL);
660  /* Return NULL representing constant TRUE */
661  return NULL;
662  }
663  }
664  }
665  /* Else return it unmodified */
666  return node;
667  }
668  if (is_andclause(node))
669  {
670  /* Recurse into AND clause */
671  List *newclauses = NIL;
672  ListCell *l;
673 
674  foreach(l, ((BoolExpr *) node)->args)
675  {
676  Node *oldclause = (Node *) lfirst(l);
677  Node *newclause;
678 
679  newclause = pull_up_sublinks_qual_recurse(root,
680  oldclause,
681  jtlink1,
682  available_rels1,
683  jtlink2,
684  available_rels2);
685  if (newclause)
686  newclauses = lappend(newclauses, newclause);
687  }
688  /* We might have got back fewer clauses than we started with */
689  if (newclauses == NIL)
690  return NULL;
691  else if (list_length(newclauses) == 1)
692  return (Node *) linitial(newclauses);
693  else
694  return (Node *) make_andclause(newclauses);
695  }
696  /* Stop if not an AND */
697  return node;
698 }
Expr * make_andclause(List *andclauses)
Definition: makefuncs.c:639
static bool is_andclause(const void *clause)
Definition: nodeFuncs.h:105
static Expr * get_notclausearg(const void *notclause)
Definition: nodeFuncs.h:132
static bool is_notclause(const void *clause)
Definition: nodeFuncs.h:123
@ ANY_SUBLINK
Definition: primnodes.h:934
@ EXISTS_SUBLINK
Definition: primnodes.h:932
JoinExpr * convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, Relids available_rels)
Definition: subselect.c:1267
JoinExpr * convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, bool under_not, Relids available_rels)
Definition: subselect.c:1384

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(), and SubLink::subLinkType.

Referenced by pull_up_sublinks_jointree_recurse().

◆ pull_up_subqueries()

void pull_up_subqueries ( PlannerInfo root)

Definition at line 768 of file prepjointree.c.

769 {
770  /* Top level of jointree must always be a FromExpr */
771  Assert(IsA(root->parse->jointree, FromExpr));
772  /* Recursion starts with no containing join nor appendrel */
773  root->parse->jointree = (FromExpr *)
775  NULL, NULL);
776  /* We should still have a FromExpr */
777  Assert(IsA(root->parse->jointree, FromExpr));
778 }
static Node * pull_up_subqueries_recurse(PlannerInfo *root, Node *jtnode, JoinExpr *lowest_outer_join, AppendRelInfo *containing_appendrel)
Definition: prepjointree.c:812

References Assert(), IsA, Query::jointree, PlannerInfo::parse, and pull_up_subqueries_recurse().

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 812 of file prepjointree.c.

815 {
816  /* Since this function recurses, it could be driven to stack overflow. */
818  /* Also, since it's a bit expensive, let's check for query cancel. */
820 
821  Assert(jtnode != NULL);
822  if (IsA(jtnode, RangeTblRef))
823  {
824  int varno = ((RangeTblRef *) jtnode)->rtindex;
825  RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable);
826 
827  /*
828  * Is this a subquery RTE, and if so, is the subquery simple enough to
829  * pull up?
830  *
831  * If we are looking at an append-relation member, we can't pull it up
832  * unless is_safe_append_member says so.
833  */
834  if (rte->rtekind == RTE_SUBQUERY &&
835  is_simple_subquery(root, rte->subquery, rte, lowest_outer_join) &&
836  (containing_appendrel == NULL ||
838  return pull_up_simple_subquery(root, jtnode, rte,
839  lowest_outer_join,
840  containing_appendrel);
841 
842  /*
843  * Alternatively, is it a simple UNION ALL subquery? If so, flatten
844  * into an "append relation".
845  *
846  * It's safe to do this regardless of whether this query is itself an
847  * appendrel member. (If you're thinking we should try to flatten the
848  * two levels of appendrel together, you're right; but we handle that
849  * in set_append_rel_pathlist, not here.)
850  */
851  if (rte->rtekind == RTE_SUBQUERY &&
853  return pull_up_simple_union_all(root, jtnode, rte);
854 
855  /*
856  * Or perhaps it's a simple VALUES RTE?
857  *
858  * We don't allow VALUES pullup below an outer join nor into an
859  * appendrel (such cases are impossible anyway at the moment).
860  */
861  if (rte->rtekind == RTE_VALUES &&
862  lowest_outer_join == NULL &&
863  containing_appendrel == NULL &&
864  is_simple_values(root, rte))
865  return pull_up_simple_values(root, jtnode, rte);
866 
867  /*
868  * Or perhaps it's a FUNCTION RTE that we could inline?
869  */
870  if (rte->rtekind == RTE_FUNCTION)
871  return pull_up_constant_function(root, jtnode, rte,
872  containing_appendrel);
873 
874  /* Otherwise, do nothing at this node. */
875  }
876  else if (IsA(jtnode, FromExpr))
877  {
878  FromExpr *f = (FromExpr *) jtnode;
879  ListCell *l;
880 
881  Assert(containing_appendrel == NULL);
882  /* Recursively transform all the child nodes */
883  foreach(l, f->fromlist)
884  {
886  lowest_outer_join,
887  NULL);
888  }
889  }
890  else if (IsA(jtnode, JoinExpr))
891  {
892  JoinExpr *j = (JoinExpr *) jtnode;
893 
894  Assert(containing_appendrel == NULL);
895  /* Recurse, being careful to tell myself when inside outer join */
896  switch (j->jointype)
897  {
898  case JOIN_INNER:
899  j->larg = pull_up_subqueries_recurse(root, j->larg,
900  lowest_outer_join,
901  NULL);
902  j->rarg = pull_up_subqueries_recurse(root, j->rarg,
903  lowest_outer_join,
904  NULL);
905  break;
906  case JOIN_LEFT:
907  case JOIN_SEMI:
908  case JOIN_ANTI:
909  j->larg = pull_up_subqueries_recurse(root, j->larg,
910  j,
911  NULL);
912  j->rarg = pull_up_subqueries_recurse(root, j->rarg,
913  j,
914  NULL);
915  break;
916  case JOIN_FULL:
917  j->larg = pull_up_subqueries_recurse(root, j->larg,
918  j,
919  NULL);
920  j->rarg = pull_up_subqueries_recurse(root, j->rarg,
921  j,
922  NULL);
923  break;
924  case JOIN_RIGHT:
925  j->larg = pull_up_subqueries_recurse(root, j->larg,
926  j,
927  NULL);
928  j->rarg = pull_up_subqueries_recurse(root, j->rarg,
929  j,
930  NULL);
931  break;
932  default:
933  elog(ERROR, "unrecognized join type: %d",
934  (int) j->jointype);
935  break;
936  }
937  }
938  else
939  elog(ERROR, "unrecognized node type: %d",
940  (int) nodeTag(jtnode));
941  return jtnode;
942 }
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
@ JOIN_SEMI
Definition: nodes.h:297
@ JOIN_ANTI
Definition: nodes.h:298
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)
Definition: prepjointree.c:957
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, PlannerInfo::parse, pull_up_constant_function(), pull_up_simple_subquery(), pull_up_simple_union_all(), pull_up_simple_values(), rt_fetch, Query::rtable, 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 1379 of file prepjointree.c.

1381 {
1382  if (IsA(setOp, RangeTblRef))
1383  {
1384  RangeTblRef *rtr = (RangeTblRef *) setOp;
1385  int childRTindex;
1386  AppendRelInfo *appinfo;
1387 
1388  /*
1389  * Calculate the index in the parent's range table
1390  */
1391  childRTindex = childRToffset + rtr->rtindex;
1392 
1393  /*
1394  * Build a suitable AppendRelInfo, and attach to parent's list.
1395  */
1396  appinfo = makeNode(AppendRelInfo);
1397  appinfo->parent_relid = parentRTindex;
1398  appinfo->child_relid = childRTindex;
1399  appinfo->parent_reltype = InvalidOid;
1400  appinfo->child_reltype = InvalidOid;
1401  make_setop_translation_list(setOpQuery, childRTindex, appinfo);
1402  appinfo->parent_reloid = InvalidOid;
1403  root->append_rel_list = lappend(root->append_rel_list, appinfo);
1404 
1405  /*
1406  * Recursively apply pull_up_subqueries to the new child RTE. (We
1407  * must build the AppendRelInfo first, because this will modify it;
1408  * indeed, that's the only part of the upper query where Vars
1409  * referencing childRTindex can exist at this point.)
1410  *
1411  * Note that we can pass NULL for containing-join info even if we're
1412  * actually under an outer join, because the child's expressions
1413  * aren't going to propagate up to the join. Also, we ignore the
1414  * possibility that pull_up_subqueries_recurse() returns a different
1415  * jointree node than what we pass it; if it does, the important thing
1416  * is that it replaced the child relid in the AppendRelInfo node.
1417  */
1418  rtr = makeNode(RangeTblRef);
1419  rtr->rtindex = childRTindex;
1420  (void) pull_up_subqueries_recurse(root, (Node *) rtr,
1421  NULL, appinfo);
1422  }
1423  else if (IsA(setOp, SetOperationStmt))
1424  {
1425  SetOperationStmt *op = (SetOperationStmt *) setOp;
1426 
1427  /* Recurse to reach leaf queries */
1428  pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery,
1429  childRToffset);
1430  pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery,
1431  childRToffset);
1432  }
1433  else
1434  {
1435  elog(ERROR, "unrecognized node type: %d",
1436  (int) nodeTag(setOp));
1437  }
1438 }
#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:2952

References PlannerInfo::append_rel_list, 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, 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 2299 of file prepjointree.c.

2300 {
2301  return replace_rte_variables(expr,
2302  context->varno, 0,
2304  (void *) context,
2305  context->outer_hasSubLinks);
2306 }
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 pullup_replace_vars_context::outer_hasSubLinks, pullup_replace_vars_callback(), replace_rte_variables(), and pullup_replace_vars_context::varno.

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 2309 of file prepjointree.c.

2311 {
2313  int varattno = var->varattno;
2314  bool need_phv;
2315  Node *newnode;
2316 
2317  /*
2318  * We need a PlaceHolderVar if the Var-to-be-replaced has nonempty
2319  * varnullingrels (unless we find below that the replacement expression is
2320  * a Var or PlaceHolderVar that we can just add the nullingrels to). We
2321  * also need one if the caller has instructed us that all non-Var/PHV
2322  * replacements need to be wrapped for identification purposes.
2323  */
2324  need_phv = (var->varnullingrels != NULL) || rcon->wrap_non_vars;
2325 
2326  /*
2327  * If PlaceHolderVars are needed, we cache the modified expressions in
2328  * rcon->rv_cache[]. This is not in hopes of any material speed gain
2329  * within this function, but to avoid generating identical PHVs with
2330  * different IDs. That would result in duplicate evaluations at runtime,
2331  * and possibly prevent optimizations that rely on recognizing different
2332  * references to the same subquery output as being equal(). So it's worth
2333  * a bit of extra effort to avoid it.
2334  *
2335  * The cached items have phlevelsup = 0 and phnullingrels = NULL; we'll
2336  * copy them and adjust those values for this reference site below.
2337  */
2338  if (need_phv &&
2339  varattno >= InvalidAttrNumber &&
2340  varattno <= list_length(rcon->targetlist) &&
2341  rcon->rv_cache[varattno] != NULL)
2342  {
2343  /* Just copy the entry and fall through to adjust phlevelsup etc */
2344  newnode = copyObject(rcon->rv_cache[varattno]);
2345  }
2346  else if (varattno == InvalidAttrNumber)
2347  {
2348  /* Must expand whole-tuple reference into RowExpr */
2349  RowExpr *rowexpr;
2350  List *colnames;
2351  List *fields;
2352  bool save_wrap_non_vars = rcon->wrap_non_vars;
2353  int save_sublevelsup = context->sublevels_up;
2354 
2355  /*
2356  * If generating an expansion for a var of a named rowtype (ie, this
2357  * is a plain relation RTE), then we must include dummy items for
2358  * dropped columns. If the var is RECORD (ie, this is a JOIN), then
2359  * omit dropped columns. In the latter case, attach column names to
2360  * the RowExpr for use of the executor and ruleutils.c.
2361  *
2362  * In order to be able to cache the results, we always generate the
2363  * expansion with varlevelsup = 0, and then adjust below if needed.
2364  */
2365  expandRTE(rcon->target_rte,
2366  var->varno, 0 /* not varlevelsup */ , var->location,
2367  (var->vartype != RECORDOID),
2368  &colnames, &fields);
2369  /* Expand the generated per-field Vars, but don't insert PHVs there */
2370  rcon->wrap_non_vars = false;
2371  context->sublevels_up = 0; /* to match the expandRTE output */
2372  fields = (List *) replace_rte_variables_mutator((Node *) fields,
2373  context);
2374  rcon->wrap_non_vars = save_wrap_non_vars;
2375  context->sublevels_up = save_sublevelsup;
2376 
2377  rowexpr = makeNode(RowExpr);
2378  rowexpr->args = fields;
2379  rowexpr->row_typeid = var->vartype;
2380  rowexpr->row_format = COERCE_IMPLICIT_CAST;
2381  rowexpr->colnames = (var->vartype == RECORDOID) ? colnames : NIL;
2382  rowexpr->location = var->location;
2383  newnode = (Node *) rowexpr;
2384 
2385  /*
2386  * Insert PlaceHolderVar if needed. Notice that we are wrapping one
2387  * PlaceHolderVar around the whole RowExpr, rather than putting one
2388  * around each element of the row. This is because we need the
2389  * expression to yield NULL, not ROW(NULL,NULL,...) when it is forced
2390  * to null by an outer join.
2391  */
2392  if (need_phv)
2393  {
2394  newnode = (Node *)
2396  (Expr *) newnode,
2397  bms_make_singleton(rcon->varno));
2398  /* cache it with the PHV, and with phlevelsup etc not set yet */
2399  rcon->rv_cache[InvalidAttrNumber] = copyObject(newnode);
2400  }
2401  }
2402  else
2403  {
2404  /* Normal case referencing one targetlist element */
2405  TargetEntry *tle = get_tle_by_resno(rcon->targetlist, varattno);
2406 
2407  if (tle == NULL) /* shouldn't happen */
2408  elog(ERROR, "could not find attribute %d in subquery targetlist",
2409  varattno);
2410 
2411  /* Make a copy of the tlist item to return */
2412  newnode = (Node *) copyObject(tle->expr);
2413 
2414  /* Insert PlaceHolderVar if needed */
2415  if (need_phv)
2416  {
2417  bool wrap;
2418 
2419  if (newnode && IsA(newnode, Var) &&
2420  ((Var *) newnode)->varlevelsup == 0)
2421  {
2422  /*
2423  * Simple Vars always escape being wrapped, unless they are
2424  * lateral references to something outside the subquery being
2425  * pulled up. (Even then, we could omit the PlaceHolderVar if
2426  * the referenced rel is under the same lowest outer join, but
2427  * it doesn't seem worth the trouble to check that.)
2428  */
2429  if (rcon->target_rte->lateral &&
2430  !bms_is_member(((Var *) newnode)->varno, rcon->relids))
2431  wrap = true;
2432  else
2433  wrap = false;
2434  }
2435  else if (newnode && IsA(newnode, PlaceHolderVar) &&
2436  ((PlaceHolderVar *) newnode)->phlevelsup == 0)
2437  {
2438  /* The same rules apply for a PlaceHolderVar */
2439  if (rcon->target_rte->lateral &&
2440  !bms_is_subset(((PlaceHolderVar *) newnode)->phrels,
2441  rcon->relids))
2442  wrap = true;
2443  else
2444  wrap = false;
2445  }
2446  else
2447  {
2448  /*
2449  * Must wrap, either because we need a place to insert
2450  * varnullingrels or because caller told us to wrap
2451  * everything.
2452  */
2453  wrap = true;
2454  }
2455 
2456  if (wrap)
2457  {
2458  newnode = (Node *)
2460  (Expr *) newnode,
2461  bms_make_singleton(rcon->varno));
2462 
2463  /*
2464  * Cache it if possible (ie, if the attno is in range, which
2465  * it probably always should be).
2466  */
2467  if (varattno > InvalidAttrNumber &&
2468  varattno <= list_length(rcon->targetlist))
2469  rcon->rv_cache[varattno] = copyObject(newnode);
2470  }
2471  }
2472  }
2473 
2474  /* Must adjust varlevelsup if replaced Var is within a subquery */
2475  if (var->varlevelsup > 0)
2476  IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
2477 
2478  /* Propagate any varnullingrels into the replacement Var or PHV */
2479  if (var->varnullingrels != NULL)
2480  {
2481  if (IsA(newnode, Var))
2482  {
2483  Var *newvar = (Var *) newnode;
2484 
2485  Assert(newvar->varlevelsup == var->varlevelsup);
2486  newvar->varnullingrels = bms_add_members(newvar->varnullingrels,
2487  var->varnullingrels);
2488  }
2489  else if (IsA(newnode, PlaceHolderVar))
2490  {
2491  PlaceHolderVar *newphv = (PlaceHolderVar *) newnode;
2492 
2493  Assert(newphv->phlevelsup == var->varlevelsup);
2494  newphv->phnullingrels = bms_add_members(newphv->phnullingrels,
2495  var->varnullingrels);
2496  }
2497  else
2498  elog(ERROR, "failed to wrap a non-Var");
2499  }
2500 
2501  return newnode;
2502 }
#define InvalidAttrNumber
Definition: attnum.h:23
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:523
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:930
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:671
Node * replace_rte_variables_mutator(Node *node, replace_rte_variables_context *context)
Relids phnullingrels
Definition: pathnodes.h:2768
int location
Definition: primnodes.h:1370
List * args
Definition: primnodes.h:1346
Expr * expr
Definition: primnodes.h:1922
Definition: primnodes.h:234
AttrNumber varattno
Definition: primnodes.h:246
int varno
Definition: primnodes.h:241
Index varlevelsup
Definition: primnodes.h:266
int location
Definition: primnodes.h:279

References RowExpr::args, Assert(), bms_add_members(), bms_is_member(), bms_is_subset(), bms_make_singleton(), replace_rte_variables_context::callback_arg, COERCE_IMPLICIT_CAST, copyObject, elog(), ERROR, expandRTE(), TargetEntry::expr, get_tle_by_resno(), IncrementVarSublevelsUp(), InvalidAttrNumber, IsA, RangeTblEntry::lateral, list_length(), Var::location, RowExpr::location, make_placeholder_expr(), makeNode, NIL, PlaceHolderVar::phlevelsup, PlaceHolderVar::phnullingrels, pullup_replace_vars_context::relids, replace_rte_variables_mutator(), pullup_replace_vars_context::root, pullup_replace_vars_context::rv_cache, replace_rte_variables_context::sublevels_up, 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 2512 of file prepjointree.c.

2514 {
2515  Assert(IsA(query, Query));
2516  return (Query *) replace_rte_variables((Node *) query,
2517  context->varno, 1,
2519  (void *) context,
2520  NULL);
2521 }

References Assert(), IsA, pullup_replace_vars_callback(), replace_rte_variables(), and pullup_replace_vars_context::varno.

Referenced by replace_vars_in_jointree().

◆ reduce_outer_joins()

void reduce_outer_joins ( PlannerInfo root)

Definition at line 2658 of file prepjointree.c.

2659 {
2662  ListCell *lc;
2663 
2664  /*
2665  * To avoid doing strictness checks on more quals than necessary, we want
2666  * to stop descending the jointree as soon as there are no outer joins
2667  * below our current point. This consideration forces a two-pass process.
2668  * The first pass gathers information about which base rels appear below
2669  * each side of each join clause, and about whether there are outer
2670  * join(s) below each side of each join clause. The second pass examines
2671  * qual clauses and changes join types as it descends the tree.
2672  */
2673  state1 = reduce_outer_joins_pass1((Node *) root->parse->jointree);
2674 
2675  /* planner.c shouldn't have called me if no outer joins */
2676  if (state1 == NULL || !state1->contains_outer)
2677  elog(ERROR, "so where are the outer joins?");
2678 
2679  state2.inner_reduced = NULL;
2680  state2.partial_reduced = NIL;
2681 
2683  state1, &state2,
2684  root, NULL, NIL);
2685 
2686  /*
2687  * If we successfully reduced the strength of any outer joins, we must
2688  * remove references to those joins as nulling rels. This is handled as
2689  * an additional pass, for simplicity and because we can handle all
2690  * fully-reduced joins in a single pass over the parse tree.
2691  */
2692  if (!bms_is_empty(state2.inner_reduced))
2693  {
2694  root->parse = (Query *)
2695  remove_nulling_relids((Node *) root->parse,
2696  state2.inner_reduced,
2697  NULL);
2698  /* There could be references in the append_rel_list, too */
2699  root->append_rel_list = (List *)
2701  state2.inner_reduced,
2702  NULL);
2703  }
2704 
2705  /*
2706  * Partially-reduced full joins have to be done one at a time, since
2707  * they'll each need a different setting of except_relids.
2708  */
2709  foreach(lc, state2.partial_reduced)
2710  {
2712  Relids full_join_relids = bms_make_singleton(statep->full_join_rti);
2713 
2714  root->parse = (Query *)
2715  remove_nulling_relids((Node *) root->parse,
2716  full_join_relids,
2717  statep->unreduced_side);
2718  root->append_rel_list = (List *)
2720  full_join_relids,
2721  statep->unreduced_side);
2722  }
2723 }
#define bms_is_empty(a)
Definition: bitmapset.h:105
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 PlannerInfo::append_rel_list, 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, Query::jointree, lfirst, NIL, PlannerInfo::parse, reduce_outer_joins_pass2_state::partial_reduced, reduce_outer_joins_pass1(), reduce_outer_joins_pass2(), remove_nulling_relids(), 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 2731 of file prepjointree.c.

2732 {
2734 
2735  result = (reduce_outer_joins_pass1_state *)
2737  result->relids = NULL;
2738  result->contains_outer = false;
2739  result->sub_states = NIL;
2740 
2741  if (jtnode == NULL)
2742  return result;
2743  if (IsA(jtnode, RangeTblRef))
2744  {
2745  int varno = ((RangeTblRef *) jtnode)->rtindex;
2746 
2747  result->relids = bms_make_singleton(varno);
2748  }
2749  else if (IsA(jtnode, FromExpr))
2750  {
2751  FromExpr *f = (FromExpr *) jtnode;
2752  ListCell *l;
2753 
2754  foreach(l, f->fromlist)
2755  {
2756  reduce_outer_joins_pass1_state *sub_state;
2757 
2758  sub_state = reduce_outer_joins_pass1(lfirst(l));
2759  result->relids = bms_add_members(result->relids,
2760  sub_state->relids);
2761  result->contains_outer |= sub_state->contains_outer;
2762  result->sub_states = lappend(result->sub_states, sub_state);
2763  }
2764  }
2765  else if (IsA(jtnode, JoinExpr))
2766  {
2767  JoinExpr *j = (JoinExpr *) jtnode;
2768  reduce_outer_joins_pass1_state *sub_state;
2769 
2770  /* join's own RT index is not wanted in result->relids */
2771  if (IS_OUTER_JOIN(j->jointype))
2772  result->contains_outer = true;
2773 
2774  sub_state = reduce_outer_joins_pass1(j->larg);
2775  result->relids = bms_add_members(result->relids,
2776  sub_state->relids);
2777  result->contains_outer |= sub_state->contains_outer;
2778  result->sub_states = lappend(result->sub_states, sub_state);
2779 
2780  sub_state = reduce_outer_joins_pass1(j->rarg);
2781  result->relids = bms_add_members(result->relids,
2782  sub_state->relids);
2783  result->contains_outer |= sub_state->contains_outer;
2784  result->sub_states = lappend(result->sub_states, sub_state);
2785  }
2786  else
2787  elog(ERROR, "unrecognized node type: %d",
2788  (int) nodeTag(jtnode));
2789  return result;
2790 }
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:327

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 2809 of file prepjointree.c.

2815 {
2816  /*
2817  * pass 2 should never descend as far as an empty subnode or base rel,
2818  * because it's only called on subtrees marked as contains_outer.
2819  */
2820  if (jtnode == NULL)
2821  elog(ERROR, "reached empty jointree");
2822  if (IsA(jtnode, RangeTblRef))
2823  elog(ERROR, "reached base rel");
2824  else if (IsA(jtnode, FromExpr))
2825  {
2826  FromExpr *f = (FromExpr *) jtnode;
2827  ListCell *l;
2828  ListCell *s;
2829  Relids pass_nonnullable_rels;
2830  List *pass_forced_null_vars;
2831 
2832  /* Scan quals to see if we can add any constraints */
2833  pass_nonnullable_rels = find_nonnullable_rels(f->quals);
2834  pass_nonnullable_rels = bms_add_members(pass_nonnullable_rels,
2835  nonnullable_rels);
2836  pass_forced_null_vars = find_forced_null_vars(f->quals);
2837  pass_forced_null_vars = mbms_add_members(pass_forced_null_vars,
2838  forced_null_vars);
2839  /* And recurse --- but only into interesting subtrees */
2841  forboth(l, f->fromlist, s, state1->sub_states)
2842  {
2843  reduce_outer_joins_pass1_state *sub_state = lfirst(s);
2844 
2845  if (sub_state->contains_outer)
2846  reduce_outer_joins_pass2(lfirst(l), sub_state,
2847  state2, root,
2848  pass_nonnullable_rels,
2849  pass_forced_null_vars);
2850  }
2851  bms_free(pass_nonnullable_rels);
2852  /* can't so easily clean up var lists, unfortunately */
2853  }
2854  else if (IsA(jtnode, JoinExpr))
2855  {
2856  JoinExpr *j = (JoinExpr *) jtnode;
2857  int rtindex = j->rtindex;
2858  JoinType jointype = j->jointype;
2859  reduce_outer_joins_pass1_state *left_state = linitial(state1->sub_states);
2860  reduce_outer_joins_pass1_state *right_state = lsecond(state1->sub_states);
2861 
2862  /* Can we simplify this join? */
2863  switch (jointype)
2864  {
2865  case JOIN_INNER:
2866  break;
2867  case JOIN_LEFT:
2868  if (bms_overlap(nonnullable_rels, right_state->relids))
2869  jointype = JOIN_INNER;
2870  break;
2871  case JOIN_RIGHT:
2872  if (bms_overlap(nonnullable_rels, left_state->relids))
2873  jointype = JOIN_INNER;
2874  break;
2875  case JOIN_FULL:
2876  if (bms_overlap(nonnullable_rels, left_state->relids))
2877  {
2878  if (bms_overlap(nonnullable_rels, right_state->relids))
2879  jointype = JOIN_INNER;
2880  else
2881  {
2882  jointype = JOIN_LEFT;
2883  /* Also report partial reduction in state2 */
2884  report_reduced_full_join(state2, rtindex,
2885  right_state->relids);
2886  }
2887  }
2888  else
2889  {
2890  if (bms_overlap(nonnullable_rels, right_state->relids))
2891  {
2892  jointype = JOIN_RIGHT;
2893  /* Also report partial reduction in state2 */
2894  report_reduced_full_join(state2, rtindex,
2895  left_state->relids);
2896  }
2897  }
2898  break;
2899  case JOIN_SEMI:
2900  case JOIN_ANTI:
2901 
2902  /*
2903  * These could only have been introduced by pull_up_sublinks,
2904  * so there's no way that upper quals could refer to their
2905  * righthand sides, and no point in checking. We don't expect
2906  * to see JOIN_RIGHT_ANTI yet.
2907  */
2908  break;
2909  default:
2910  elog(ERROR, "unrecognized join type: %d",
2911  (int) jointype);
2912  break;
2913  }
2914 
2915  /*
2916  * Convert JOIN_RIGHT to JOIN_LEFT. Note that in the case where we
2917  * reduced JOIN_FULL to JOIN_RIGHT, this will mean the JoinExpr no
2918  * longer matches the internal ordering of any CoalesceExpr's built to
2919  * represent merged join variables. We don't care about that at
2920  * present, but be wary of it ...
2921  */
2922  if (jointype == JOIN_RIGHT)
2923  {
2924  Node *tmparg;
2925 
2926  tmparg = j->larg;
2927  j->larg = j->rarg;
2928  j->rarg = tmparg;
2929  jointype = JOIN_LEFT;
2930  right_state = linitial(state1->sub_states);
2931  left_state = lsecond(state1->sub_states);
2932  }
2933 
2934  /*
2935  * See if we can reduce JOIN_LEFT to JOIN_ANTI. This is the case if
2936  * the join's own quals are strict for any var that was forced null by
2937  * higher qual levels. NOTE: there are other ways that we could
2938  * detect an anti-join, in particular if we were to check whether Vars
2939  * coming from the RHS must be non-null because of table constraints.
2940  * That seems complicated and expensive though (in particular, one
2941  * would have to be wary of lower outer joins). For the moment this
2942  * seems sufficient.
2943  */
2944  if (jointype == JOIN_LEFT)
2945  {
2946  List *nonnullable_vars;
2947  Bitmapset *overlap;
2948 
2949  /* Find Vars in j->quals that must be non-null in joined rows */
2950  nonnullable_vars = find_nonnullable_vars(j->quals);
2951 
2952  /*
2953  * It's not sufficient to check whether nonnullable_vars and
2954  * forced_null_vars overlap: we need to know if the overlap
2955  * includes any RHS variables.
2956  */
2957  overlap = mbms_overlap_sets(nonnullable_vars, forced_null_vars);
2958  if (bms_overlap(overlap, right_state->relids))
2959  jointype = JOIN_ANTI;
2960  }
2961 
2962  /*
2963  * Apply the jointype change, if any, to both jointree node and RTE.
2964  * Also, if we changed an RTE to INNER, add its RTI to inner_reduced.
2965  */
2966  if (rtindex && jointype != j->jointype)
2967  {
2968  RangeTblEntry *rte = rt_fetch(rtindex, root->parse->rtable);
2969 
2970  Assert(rte->rtekind == RTE_JOIN);
2971  Assert(rte->jointype == j->jointype);
2972  rte->jointype = jointype;
2973  if (jointype == JOIN_INNER)
2974  state2->inner_reduced = bms_add_member(state2->inner_reduced,
2975  rtindex);
2976  }
2977  j->jointype = jointype;
2978 
2979  /* Only recurse if there's more to do below here */
2980  if (left_state->contains_outer || right_state->contains_outer)
2981  {
2982  Relids local_nonnullable_rels;
2983  List *local_forced_null_vars;
2984  Relids pass_nonnullable_rels;
2985  List *pass_forced_null_vars;
2986 
2987  /*
2988  * If this join is (now) inner, we can add any constraints its
2989  * quals provide to those we got from above. But if it is outer,
2990  * we can pass down the local constraints only into the nullable
2991  * side, because an outer join never eliminates any rows from its
2992  * non-nullable side. Also, there is no point in passing upper
2993  * constraints into the nullable side, since if there were any
2994  * we'd have been able to reduce the join. (In the case of upper
2995  * forced-null constraints, we *must not* pass them into the
2996  * nullable side --- they either applied here, or not.) The upshot
2997  * is that we pass either the local or the upper constraints,
2998  * never both, to the children of an outer join.
2999  *
3000  * Note that a SEMI join works like an inner join here: it's okay
3001  * to pass down both local and upper constraints. (There can't be
3002  * any upper constraints affecting its inner side, but it's not
3003  * worth having a separate code path to avoid passing them.)
3004  *
3005  * At a FULL join we just punt and pass nothing down --- is it
3006  * possible to be smarter?
3007  */
3008  if (jointype != JOIN_FULL)
3009  {
3010  local_nonnullable_rels = find_nonnullable_rels(j->quals);
3011  local_forced_null_vars = find_forced_null_vars(j->quals);
3012  if (jointype == JOIN_INNER || jointype == JOIN_SEMI)
3013  {
3014  /* OK to merge upper and local constraints */
3015  local_nonnullable_rels = bms_add_members(local_nonnullable_rels,
3016  nonnullable_rels);
3017  local_forced_null_vars = mbms_add_members(local_forced_null_vars,
3018  forced_null_vars);
3019  }
3020  }
3021  else
3022  {
3023  /* no use in calculating these */
3024  local_nonnullable_rels = NULL;
3025  local_forced_null_vars = NIL;
3026  }
3027 
3028  if (left_state->contains_outer)
3029  {
3030  if (jointype == JOIN_INNER || jointype == JOIN_SEMI)
3031  {
3032  /* pass union of local and upper constraints */
3033  pass_nonnullable_rels = local_nonnullable_rels;
3034  pass_forced_null_vars = local_forced_null_vars;
3035  }
3036  else if (jointype != JOIN_FULL) /* ie, LEFT or ANTI */
3037  {
3038  /* can't pass local constraints to non-nullable side */
3039  pass_nonnullable_rels = nonnullable_rels;
3040  pass_forced_null_vars = forced_null_vars;
3041  }
3042  else
3043  {
3044  /* no constraints pass through JOIN_FULL */
3045  pass_nonnullable_rels = NULL;
3046  pass_forced_null_vars = NIL;
3047  }
3048  reduce_outer_joins_pass2(j->larg, left_state,
3049  state2, root,
3050  pass_nonnullable_rels,
3051  pass_forced_null_vars);
3052  }
3053 
3054  if (right_state->contains_outer)
3055  {
3056  if (jointype != JOIN_FULL) /* ie, INNER/LEFT/SEMI/ANTI */
3057  {
3058  /* pass appropriate constraints, per comment above */
3059  pass_nonnullable_rels = local_nonnullable_rels;
3060  pass_forced_null_vars = local_forced_null_vars;
3061  }
3062  else
3063  {
3064  /* no constraints pass through JOIN_FULL */
3065  pass_nonnullable_rels = NULL;
3066  pass_forced_null_vars = NIL;
3067  }
3068  reduce_outer_joins_pass2(j->rarg, right_state,
3069  state2, root,
3070  pass_nonnullable_rels,
3071  pass_forced_null_vars);
3072  }
3073  bms_free(local_nonnullable_rels);
3074  }
3075  }
3076  else
3077  elog(ERROR, "unrecognized node type: %d",
3078  (int) nodeTag(jtnode));
3079 }
void bms_free(Bitmapset *a)
Definition: bitmapset.c:252
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:595
List * find_forced_null_vars(Node *node)
Definition: clauses.c:1899
Relids find_nonnullable_rels(Node *clause)
Definition: clauses.c:1439
List * find_nonnullable_vars(Node *clause)
Definition: clauses.c:1690
Bitmapset * mbms_overlap_sets(const List *a, const List *b)
List * mbms_add_members(List *a, const List *b)
JoinType
Definition: nodes.h:278
#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:1119

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, PlannerInfo::parse, FromExpr::quals, reduce_outer_joins_pass1_state::relids, report_reduced_full_join(), rt_fetch, Query::rtable, 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 3526 of file prepjointree.c.

3527 {
3528  /* Fix up PlaceHolderVars as needed */
3529  /* If there are no PHVs anywhere, we can skip this bit */
3530  if (root->glob->lastPHId != 0)
3531  {
3532  Relids subrelids;
3533 
3534  subrelids = get_relids_in_jointree(newjtloc, true, false);
3535  Assert(!bms_is_empty(subrelids));
3536  substitute_phv_relids((Node *) root->parse, varno, subrelids);
3537  fix_append_rel_relids(root, varno, subrelids);
3538  }
3539 
3540  /*
3541  * We also need to remove any PlanRowMark referencing the RTE, but we
3542  * postpone that work until we return to remove_useless_result_rtes.
3543  */
3544 }

References Assert(), bms_is_empty, fix_append_rel_relids(), get_relids_in_jointree(), PlannerInfo::glob, PlannerGlobal::lastPHId, PlannerInfo::parse, and substitute_phv_relids().

Referenced by remove_useless_results_recurse().

◆ remove_useless_result_rtes()

void remove_useless_result_rtes ( PlannerInfo root)

Definition at line 3152 of file prepjointree.c.

3153 {
3154  Relids dropped_outer_joins = NULL;
3155  ListCell *cell;
3156 
3157  /* Top level of jointree must always be a FromExpr */
3158  Assert(IsA(root->parse->jointree, FromExpr));
3159  /* Recurse ... */
3160  root->parse->jointree = (FromExpr *)
3162  (Node *) root->parse->jointree,
3163  NULL,
3164  &dropped_outer_joins);
3165  /* We should still have a FromExpr */
3166  Assert(IsA(root->parse->jointree, FromExpr));
3167 
3168  /*
3169  * If we removed any outer-join nodes from the jointree, run around and
3170  * remove references to those joins as nulling rels. (There could be such
3171  * references in PHVs that we pulled up out of the original subquery that
3172  * the RESULT rel replaced. This is kosher on the grounds that we now
3173  * know that such an outer join wouldn't really have nulled anything.) We
3174  * don't do this during the main recursion, for simplicity and because we
3175  * can handle all such joins in a single pass over the parse tree.
3176  */
3177  if (!bms_is_empty(dropped_outer_joins))
3178  {
3179  root->parse = (Query *)
3180  remove_nulling_relids((Node *) root->parse,
3181  dropped_outer_joins,
3182  NULL);
3183  /* There could be references in the append_rel_list, too */
3184  root->append_rel_list = (List *)
3186  dropped_outer_joins,
3187  NULL);
3188  }
3189 
3190  /*
3191  * Remove any PlanRowMark referencing an RTE_RESULT RTE. We obviously
3192  * must do that for any RTE_RESULT that we just removed. But one for a
3193  * RTE that we did not remove can be dropped anyway: since the RTE has
3194  * only one possible output row, there is no need for EPQ to mark and
3195  * restore that row.
3196  *
3197  * It's necessary, not optional, to remove the PlanRowMark for a surviving
3198  * RTE_RESULT RTE; otherwise we'll generate a whole-row Var for the
3199  * RTE_RESULT, which the executor has no support for.
3200  */
3201  foreach(cell, root->rowMarks)
3202  {
3203  PlanRowMark *rc = (PlanRowMark *) lfirst(cell);
3204 
3205  if (rt_fetch(rc->rti, root->parse->rtable)->rtekind == RTE_RESULT)
3206  root->rowMarks = foreach_delete_current(root->rowMarks, cell);
3207  }
3208 }
#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 PlannerInfo::append_rel_list, Assert(), bms_is_empty, foreach_delete_current, IsA, Query::jointree, lfirst, PlannerInfo::parse, remove_nulling_relids(), remove_useless_results_recurse(), PlannerInfo::rowMarks, rt_fetch, Query::rtable, 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 3225 of file prepjointree.c.

3228 {
3229  Assert(jtnode != NULL);
3230  if (IsA(jtnode, RangeTblRef))
3231  {
3232  /* Can't immediately do anything with a RangeTblRef */
3233  }
3234  else if (IsA(jtnode, FromExpr))
3235  {
3236  FromExpr *f = (FromExpr *) jtnode;
3237  Relids result_relids = NULL;
3238  ListCell *cell;
3239 
3240  /*
3241  * We can drop RTE_RESULT rels from the fromlist so long as at least
3242  * one child remains, since joining to a one-row table changes
3243  * nothing. (But we can't drop a RTE_RESULT that computes PHV(s) that
3244  * are needed by some sibling. The cleanup transformation below would
3245  * reassign the PHVs to be computed at the join, which is too late for
3246  * the sibling's use.) The easiest way to mechanize this rule is to
3247  * modify the list in-place.
3248  */
3249  foreach(cell, f->fromlist)
3250  {
3251  Node *child = (Node *) lfirst(cell);
3252  int varno;
3253 
3254  /* Recursively transform child, allowing it to push up quals ... */
3255  child = remove_useless_results_recurse(root, child,
3256  &f->quals,
3257  dropped_outer_joins);
3258  /* ... and stick it back into the tree */
3259  lfirst(cell) = child;
3260 
3261  /*
3262  * If it's an RTE_RESULT with at least one sibling, and no sibling
3263  * references dependent PHVs, we can drop it. We don't yet know
3264  * what the inner join's final relid set will be, so postpone
3265  * cleanup of PHVs etc till after this loop.
3266  */
3267  if (list_length(f->fromlist) > 1 &&
3268  (varno = get_result_relid(root, child)) != 0 &&
3269  !find_dependent_phvs_in_jointree(root, (Node *) f, varno))
3270  {
3271  f->fromlist = foreach_delete_current(f->fromlist, cell);
3272  result_relids = bms_add_member(result_relids, varno);
3273  }
3274  }
3275 
3276  /*
3277  * Clean up if we dropped any RTE_RESULT RTEs. This is a bit
3278  * inefficient if there's more than one, but it seems better to
3279  * optimize the support code for the single-relid case.
3280  */
3281  if (result_relids)
3282  {
3283  int varno = -1;
3284 
3285  while ((varno = bms_next_member(result_relids, varno)) >= 0)
3286  remove_result_refs(root, varno, (Node *) f);
3287  }
3288 
3289  /*
3290  * If the FromExpr now has only one child, see if we can elide it.
3291  * This is always valid if there are no quals, except at the top of
3292  * the jointree (since Query.jointree is required to point to a
3293  * FromExpr). Otherwise, we can do it if we can push the quals up to
3294  * the parent node.
3295  *
3296  * Note: while it would not be terribly hard to generalize this
3297  * transformation to merge multi-child FromExprs into their parent
3298  * FromExpr, that risks making the parent join too expensive to plan.
3299  * We leave it to later processing to decide heuristically whether
3300  * that's a good idea. Pulling up a single child is always OK,
3301  * however.
3302  */
3303  if (list_length(f->fromlist) == 1 &&
3304  f != root->parse->jointree &&
3305  (f->quals == NULL || parent_quals != NULL))
3306  {
3307  /*
3308  * Merge any quals up to parent. They should be in implicit-AND
3309  * format by now, so we just need to concatenate lists. Put the
3310  * child quals at the front, on the grounds that they should
3311  * nominally be evaluated earlier.
3312  */
3313  if (f->quals != NULL)
3314  *parent_quals = (Node *)
3316  castNode(List, *parent_quals));
3317  return (Node *) linitial(f->fromlist);
3318  }
3319  }
3320  else if (IsA(jtnode, JoinExpr))
3321  {
3322  JoinExpr *j = (JoinExpr *) jtnode;
3323  int varno;
3324 
3325  /*
3326  * First, recurse. We can absorb pushed-up FromExpr quals from either
3327  * child into this node if the jointype is INNER, since then this is
3328  * equivalent to a FromExpr. When the jointype is LEFT, we can absorb
3329  * quals from the RHS child into the current node, as they're
3330  * essentially degenerate quals of the outer join. Moreover, if we've
3331  * been passed down a parent_quals pointer then we can allow quals of
3332  * the LHS child to be absorbed into the parent. (This is important
3333  * to ensure we remove single-child FromExprs immediately below
3334  * commutable left joins.) For other jointypes, we can't move child
3335  * quals up, or at least there's no particular reason to.
3336  */
3337  j->larg = remove_useless_results_recurse(root, j->larg,
3338  (j->jointype == JOIN_INNER) ?
3339  &j->quals :
3340  (j->jointype == JOIN_LEFT) ?
3341  parent_quals : NULL,
3342  dropped_outer_joins);
3343  j->rarg = remove_useless_results_recurse(root, j->rarg,
3344  (j->jointype == JOIN_INNER ||
3345  j->jointype == JOIN_LEFT) ?
3346  &j->quals : NULL,
3347  dropped_outer_joins);
3348 
3349  /* Apply join-type-specific optimization rules */
3350  switch (j->jointype)
3351  {
3352  case JOIN_INNER:
3353 
3354  /*
3355  * An inner join is equivalent to a FromExpr, so if either
3356  * side was simplified to an RTE_RESULT rel, we can replace
3357  * the join with a FromExpr with just the other side.
3358  * Furthermore, we can elide that FromExpr according to the
3359  * same rules as above.
3360  *
3361  * Just as in the FromExpr case, we can't simplify if the
3362  * other input rel references any PHVs that are marked as to
3363  * be evaluated at the RTE_RESULT rel, because we can't
3364  * postpone their evaluation in that case. But we only have
3365  * to check this in cases where it's syntactically legal for
3366  * the other input to have a LATERAL reference to the
3367  * RTE_RESULT rel. Only RHSes of inner and left joins are
3368  * allowed to have such refs.
3369  */
3370  if ((varno = get_result_relid(root, j->larg)) != 0 &&
3371  !find_dependent_phvs_in_jointree(root, j->rarg, varno))
3372  {
3373  remove_result_refs(root, varno, j->rarg);
3374  if (j->quals != NULL && parent_quals == NULL)
3375  jtnode = (Node *)
3376  makeFromExpr(list_make1(j->rarg), j->quals);
3377  else
3378  {
3379  /* Merge any quals up to parent */
3380  if (j->quals != NULL)
3381  *parent_quals = (Node *)
3382  list_concat(castNode(List, j->quals),
3383  castNode(List, *parent_quals));
3384  jtnode = j->rarg;
3385  }
3386  }
3387  else if ((varno = get_result_relid(root, j->rarg)) != 0)
3388  {
3389  remove_result_refs(root, varno, j->larg);
3390  if (j->quals != NULL && parent_quals == NULL)
3391  jtnode = (Node *)
3392  makeFromExpr(list_make1(j->larg), j->quals);
3393  else
3394  {
3395  /* Merge any quals up to parent */
3396  if (j->quals != NULL)
3397  *parent_quals = (Node *)
3398  list_concat(castNode(List, j->quals),
3399  castNode(List, *parent_quals));
3400  jtnode = j->larg;
3401  }
3402  }
3403  break;
3404  case JOIN_LEFT:
3405 
3406  /*
3407  * We can simplify this case if the RHS is an RTE_RESULT, with
3408  * two different possibilities:
3409  *
3410  * If the qual is empty (JOIN ON TRUE), then the join can be
3411  * strength-reduced to a plain inner join, since each LHS row
3412  * necessarily has exactly one join partner. So we can always
3413  * discard the RHS, much as in the JOIN_INNER case above.
3414  * (Again, the LHS could not contain a lateral reference to
3415  * the RHS.)
3416  *
3417  * Otherwise, it's still true that each LHS row should be
3418  * returned exactly once, and since the RHS returns no columns
3419  * (unless there are PHVs that have to be evaluated there), we
3420  * don't much care if it's null-extended or not. So in this
3421  * case also, we can just ignore the qual and discard the left
3422  * join.
3423  */
3424  if ((varno = get_result_relid(root, j->rarg)) != 0 &&
3425  (j->quals == NULL ||
3426  !find_dependent_phvs(root, varno)))
3427  {
3428  remove_result_refs(root, varno, j->larg);
3429  *dropped_outer_joins = bms_add_member(*dropped_outer_joins,
3430  j->rtindex);
3431  jtnode = j->larg;
3432  }
3433  break;
3434  case JOIN_SEMI:
3435 
3436  /*
3437  * We may simplify this case if the RHS is an RTE_RESULT; the
3438  * join qual becomes effectively just a filter qual for the
3439  * LHS, since we should either return the LHS row or not. The
3440  * filter clause must go into a new FromExpr if we can't push
3441  * it up to the parent.
3442  *
3443  * There is a fine point about PHVs that are supposed to be
3444  * evaluated at the RHS. Such PHVs could only appear in the
3445  * semijoin's qual, since the rest of the query cannot
3446  * reference any outputs of the semijoin's RHS. Therefore,
3447  * they can't actually go to null before being examined, and
3448  * it'd be OK to just remove the PHV wrapping. We don't have
3449  * infrastructure for that, but remove_result_refs() will
3450  * relabel them as to be evaluated at the LHS, which is fine.
3451  *
3452  * Also, we don't need to worry about removing traces of the
3453  * join's rtindex, since it hasn't got one.
3454  */
3455  if ((varno = get_result_relid(root, j->rarg)) != 0)
3456  {
3457  Assert(j->rtindex == 0);
3458  remove_result_refs(root, varno, j->larg);
3459  if (j->quals != NULL && parent_quals == NULL)
3460  jtnode = (Node *)
3461  makeFromExpr(list_make1(j->larg), j->quals);
3462  else
3463  {
3464  /* Merge any quals up to parent */
3465  if (j->quals != NULL)
3466  *parent_quals = (Node *)
3467  list_concat(castNode(List, j->quals),
3468  castNode(List, *parent_quals));
3469  jtnode = j->larg;
3470  }
3471  }
3472  break;
3473  case JOIN_FULL:
3474  case JOIN_ANTI:
3475  /* We have no special smarts for these cases */
3476  break;
3477  default:
3478  /* Note: JOIN_RIGHT should be gone at this point */
3479  elog(ERROR, "unrecognized join type: %d",
3480  (int) j->jointype);
3481  break;
3482  }
3483  }
3484  else
3485  elog(ERROR, "unrecognized node type: %d",
3486  (int) nodeTag(jtnode));
3487  return jtnode;
3488 }
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, Query::jointree, lfirst, linitial, list_concat(), list_length(), list_make1, makeFromExpr(), nodeTag, PlannerInfo::parse, FromExpr::quals, and remove_result_refs().

Referenced by remove_useless_result_rtes().

◆ replace_empty_jointree()

void replace_empty_jointree ( Query parse)

Definition at line 235 of file prepjointree.c.

236 {
237  RangeTblEntry *rte;
238  Index rti;
239  RangeTblRef *rtr;
240 
241  /* Nothing to do if jointree is already nonempty */
242  if (parse->jointree->fromlist != NIL)
243  return;
244 
245  /* We mustn't change it in the top level of a setop tree, either */
246  if (parse->setOperations)
247  return;
248 
249  /* Create suitable RTE */
250  rte = makeNode(RangeTblEntry);
251  rte->rtekind = RTE_RESULT;
252  rte->eref = makeAlias("*RESULT*", NIL);
253 
254  /* Add it to rangetable */
255  parse->rtable = lappend(parse->rtable, rte);
256  rti = list_length(parse->rtable);
257 
258  /* And jam a reference into the jointree */
259  rtr = makeNode(RangeTblRef);
260  rtr->rtindex = rti;
261  parse->jointree->fromlist = list_make1(rtr);
262 }
unsigned int Index
Definition: c.h:603

References RangeTblEntry::eref, 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 2193 of file prepjointree.c.

2195 {
2196  if (jtnode == NULL)
2197  return;
2198  if (IsA(jtnode, RangeTblRef))
2199  {
2200  /*
2201  * If the RangeTblRef refers to a LATERAL subquery (that isn't the
2202  * same subquery we're pulling up), it might contain references to the
2203  * target subquery, which we must replace. We drive this from the
2204  * jointree scan, rather than a scan of the rtable, so that we can
2205  * avoid processing no-longer-referenced RTEs.
2206  */
2207  int varno = ((RangeTblRef *) jtnode)->rtindex;
2208 
2209  if (varno != context->varno) /* ignore target subquery itself */
2210  {
2211  RangeTblEntry *rte = rt_fetch(varno, context->root->parse->rtable);
2212 
2213  Assert(rte != context->target_rte);
2214  if (rte->lateral)
2215  {
2216  switch (rte->rtekind)
2217  {
2218  case RTE_RELATION:
2219  /* shouldn't be marked LATERAL unless tablesample */
2220  Assert(rte->tablesample);
2221  rte->tablesample = (TableSampleClause *)
2223  context);
2224  break;
2225  case RTE_SUBQUERY:
2226  rte->subquery =
2228  context);
2229  break;
2230  case RTE_FUNCTION:
2231  rte->functions = (List *)
2233  context);
2234  break;
2235  case RTE_TABLEFUNC:
2236  rte->tablefunc = (TableFunc *)
2238  context);
2239  break;
2240  case RTE_VALUES:
2241  rte->values_lists = (List *)
2243  context);
2244  break;
2245  case RTE_JOIN:
2246  case RTE_CTE:
2247  case RTE_NAMEDTUPLESTORE:
2248  case RTE_RESULT:
2249  /* these shouldn't be marked LATERAL */
2250  Assert(false);
2251  break;
2252  }
2253  }
2254  }
2255  }
2256  else if (IsA(jtnode, FromExpr))
2257  {
2258  FromExpr *f = (FromExpr *) jtnode;
2259  ListCell *l;
2260 
2261  foreach(l, f->fromlist)
2262  replace_vars_in_jointree(lfirst(l), context);
2263  f->quals = pullup_replace_vars(f->quals, context);
2264  }
2265  else if (IsA(jtnode, JoinExpr))
2266  {
2267  JoinExpr *j = (JoinExpr *) jtnode;
2268  bool save_wrap_non_vars = context->wrap_non_vars;
2269 
2270  replace_vars_in_jointree(j->larg, context);
2271  replace_vars_in_jointree(j->rarg, context);
2272 
2273  /*
2274  * Use PHVs within the join quals of a full join. Otherwise, we
2275  * cannot identify which side of the join a pulled-up var-free
2276  * expression came from, which can lead to failure to make a plan at
2277  * all because none of the quals appear to be mergeable or hashable
2278  * conditions.
2279  */
2280  if (j->jointype == JOIN_FULL)
2281  context->wrap_non_vars = true;
2282 
2283  j->quals = pullup_replace_vars(j->quals, context);
2284 
2285  context->wrap_non_vars = save_wrap_non_vars;
2286  }
2287  else
2288  elog(ERROR, "unrecognized node type: %d",
2289  (int) nodeTag(jtnode));
2290 }
static Query * pullup_replace_vars_subquery(Query *query, pullup_replace_vars_context *context)
TableFunc * tablefunc
Definition: parsenodes.h:1146

References Assert(), elog(), ERROR, FromExpr::fromlist, RangeTblEntry::functions, IsA, j, JOIN_FULL, RangeTblEntry::lateral, lfirst, nodeTag, PlannerInfo::parse, pullup_replace_vars(), pullup_replace_vars_subquery(), FromExpr::quals, pullup_replace_vars_context::root, rt_fetch, Query::rtable, RTE_CTE, RTE_FUNCTION, RTE_JOIN, RTE_NAMEDTUPLESTORE, RTE_RELATION, RTE_RESULT, RTE_SUBQUERY, RTE_TABLEFUNC, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::subquery, RangeTblEntry::tablefunc, RangeTblEntry::tablesample, pullup_replace_vars_context::target_rte, RangeTblEntry::values_lists, pullup_replace_vars_context::varno, and pullup_replace_vars_context::wrap_non_vars.

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 3083 of file prepjointree.c.

3085 {
3087 
3088  statep = palloc(sizeof(reduce_outer_joins_partial_state));
3089  statep->full_join_rti = rtindex;
3090  statep->unreduced_side = relids;
3091  state2->partial_reduced = lappend(state2->partial_reduced, statep);
3092 }

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 3734 of file prepjointree.c.

3735 {
3737 
3738  context.varno = varno;
3739  context.sublevels_up = 0;
3740  context.subrelids = subrelids;
3741 
3742  /*
3743  * Must be prepared to start with a Query or a bare expression tree.
3744  */
3747  (void *) &context,
3748  0);
3749 }
#define query_or_expression_tree_walker(n, w, c, f)
Definition: nodeFuncs.h:169
static bool substitute_phv_relids_walker(Node *node, substitute_phv_relids_context *context)

References query_or_expression_tree_walker, substitute_phv_relids_context::sublevels_up, substitute_phv_relids_context::subrelids, substitute_phv_relids_walker(), and substitute_phv_relids_context::varno.

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 3690 of file prepjointree.c.

3692 {
3693  if (node == NULL)
3694  return false;
3695  if (IsA(node, PlaceHolderVar))
3696  {
3697  PlaceHolderVar *phv = (PlaceHolderVar *) node;
3698 
3699  if (phv->phlevelsup == context->sublevels_up &&
3700  bms_is_member(context->varno, phv->phrels))
3701  {
3702  phv->phrels = bms_union(phv->phrels,
3703  context->subrelids);
3704  phv->phrels = bms_del_member(phv->phrels,
3705  context->varno);
3706  /* Assert we haven't broken the PHV */
3707  Assert(!bms_is_empty(phv->phrels));
3708  }
3709  /* fall through to examine children */
3710  }
3711  if (IsA(node, Query))
3712  {
3713  /* Recurse into subselects */
3714  bool result;
3715 
3716  context->sublevels_up++;
3717  result = query_tree_walker((Query *) node,
3719  (void *) context, 0);
3720  context->sublevels_up--;
3721  return result;
3722  }
3723  /* Shouldn't need to handle planner auxiliary nodes here */
3724  Assert(!IsA(node, SpecialJoinInfo));
3725  Assert(!IsA(node, AppendRelInfo));
3726  Assert(!IsA(node, PlaceHolderInfo));
3727  Assert(!IsA(node, MinMaxAggInfo));
3728 
3730  (void *) context);
3731 }
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:881

References Assert(), bms_del_member(), bms_is_empty, bms_is_member(), bms_union(), expression_tree_walker, IsA, PlaceHolderVar::phlevelsup, query_tree_walker, substitute_phv_relids_context::sublevels_up, substitute_phv_relids_context::subrelids, and substitute_phv_relids_context::varno.

Referenced by substitute_phv_relids().

◆ transform_MERGE_to_join()

void transform_MERGE_to_join ( Query parse)

Definition at line 152 of file prepjointree.c.

153 {
154  RangeTblEntry *joinrte;
155  JoinExpr *joinexpr;
156  JoinType jointype;
157  int joinrti;
158  List *vars;
159 
160  if (parse->commandType != CMD_MERGE)
161  return;
162 
163  /* XXX probably bogus */
164  vars = NIL;
165 
166  /*
167  * When any WHEN NOT MATCHED THEN INSERT clauses exist, we need to use an
168  * outer join so that we process all unmatched tuples from the source
169  * relation. If none exist, we can use an inner join.
170  */
171  if (parse->mergeUseOuterJoin)
172  jointype = JOIN_RIGHT;
173  else
174  jointype = JOIN_INNER;
175 
176  /* Manufacture a join RTE to use. */
177  joinrte = makeNode(RangeTblEntry);
178  joinrte->rtekind = RTE_JOIN;
179  joinrte->jointype = jointype;
180  joinrte->joinmergedcols = 0;
181  joinrte->joinaliasvars = vars;
182  joinrte->joinleftcols = NIL; /* MERGE does not allow JOIN USING */
183  joinrte->joinrightcols = NIL; /* ditto */
184  joinrte->join_using_alias = NULL;
185 
186  joinrte->alias = NULL;
187  joinrte->eref = makeAlias("*MERGE*", NIL);
188  joinrte->lateral = false;
189  joinrte->inh = false;
190  joinrte->inFromCl = true;
191 
192  /*
193  * Add completed RTE to pstate's range table list, so that we know its
194  * index.
195  */
196  parse->rtable = lappend(parse->rtable, joinrte);
197  joinrti = list_length(parse->rtable);
198 
199  /*
200  * Create a JOIN between the target and the source relation.
201  */
202  joinexpr = makeNode(JoinExpr);
203  joinexpr->jointype = jointype;
204  joinexpr->isNatural = false;
205  joinexpr->larg = (Node *) makeNode(RangeTblRef);
206  ((RangeTblRef *) joinexpr->larg)->rtindex = parse->resultRelation;
207  joinexpr->rarg = linitial(parse->jointree->fromlist); /* original join */
208  joinexpr->usingClause = NIL;
209  joinexpr->join_using_alias = NULL;
210  /* The quals are removed from the jointree and into this specific join */
211  joinexpr->quals = parse->jointree->quals;
212  joinexpr->alias = NULL;
213  joinexpr->rtindex = joinrti;
214 
215  /* Make the new join be the sole entry in the query's jointree */
216  parse->jointree->fromlist = list_make1(joinexpr);
217  parse->jointree->quals = NULL;
218 }
@ CMD_MERGE
Definition: nodes.h:259
Node * quals
Definition: primnodes.h:2021
JoinType jointype
Definition: primnodes.h:2012
int rtindex
Definition: primnodes.h:2025
Node * larg
Definition: primnodes.h:2014
bool isNatural
Definition: primnodes.h:2013
Node * rarg
Definition: primnodes.h:2015
Alias * join_using_alias
Definition: parsenodes.h:1130
List * joinrightcols
Definition: parsenodes.h:1123
Alias * alias
Definition: parsenodes.h:1191
List * joinleftcols
Definition: parsenodes.h:1122

References RangeTblEntry::alias, CMD_MERGE, RangeTblEntry::eref, RangeTblEntry::inFromCl, RangeTblEntry::inh, JoinExpr::isNatural, JOIN_INNER, JOIN_RIGHT, RangeTblEntry::join_using_alias, RangeTblEntry::joinaliasvars, RangeTblEntry::joinleftcols, RangeTblEntry::joinmergedcols, RangeTblEntry::joinrightcols, RangeTblEntry::jointype, JoinExpr::jointype, lappend(), JoinExpr::larg, RangeTblEntry::lateral, linitial, list_length(), list_make1, makeAlias(), makeNode, NIL, parse(), JoinExpr::quals, JoinExpr::rarg, RTE_JOIN, RangeTblEntry::rtekind, and JoinExpr::rtindex.

Referenced by subquery_planner().