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

3597 {
3599 
3600  /* If there are no PHVs anywhere, we needn't work hard */
3601  if (root->glob->lastPHId == 0)
3602  return false;
3603 
3604  context.relids = bms_make_singleton(varno);
3605  context.sublevels_up = 0;
3606 
3607  if (query_tree_walker(root->parse,
3609  (void *) &context,
3610  0))
3611  return true;
3612  /* The append_rel_list could be populated already, so check it too */
3615  (void *) &context))
3616  return true;
3617  return false;
3618 }
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:171
#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 3621 of file prepjointree.c.

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

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

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

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

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

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

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

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

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

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

3863 {
3864  Node *jtnode;
3865 
3866  jtnode = find_jointree_node_for_rel((Node *) query->jointree,
3867  joinrelid);
3868  if (!jtnode)
3869  elog(ERROR, "could not find join node %d", joinrelid);
3870  return get_relids_in_jointree(jtnode, true, false);
3871 }
FromExpr * jointree
Definition: parsenodes.h:181

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

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

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

3492 {
3493  int varno;
3494 
3495  if (!IsA(jtnode, RangeTblRef))
3496  return 0;
3497  varno = ((RangeTblRef *) jtnode)->rtindex;
3498  if (rt_fetch(varno, root->parse->rtable)->rtekind != RTE_RESULT)
3499  return 0;
3500  return varno;
3501 }
@ RTE_RESULT
Definition: parsenodes.h:1021

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:2014

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:363
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:483
@ CMD_SELECT
Definition: nodes.h:276
static bool jointree_contains_lateral_outer_refs(PlannerInfo *root, Node *jtnode, bool restricted, Relids safe_upper_varnos)
Node * limitCount
Definition: parsenodes.h:211
Node * setOperations
Definition: parsenodes.h:216
List * cteList
Definition: parsenodes.h:172
List * groupClause
Definition: parsenodes.h:197
Node * havingQual
Definition: parsenodes.h:202
Node * limitOffset
Definition: parsenodes.h:210
CmdType commandType
Definition: parsenodes.h:127
List * targetList
Definition: parsenodes.h:188
List * groupingSets
Definition: parsenodes.h:200
List * distinctClause
Definition: parsenodes.h:206
List * sortClause
Definition: parsenodes.h:208
bool security_barrier
Definition: parsenodes.h:1081
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:214

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:1973
void check_stack_depth(void)
Definition: postgres.c:3523
Query * subquery
Definition: parsenodes.h:1080
SetOperation op
Definition: parsenodes.h:2050
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:1018
List * values_lists
Definition: parsenodes.h:1158

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:1257
int num_child_cols
Definition: pathnodes.h:2964
Definition: pg_list.h:54
AttrNumber resno
Definition: primnodes.h:1897
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:1015
#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:1128

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:4958
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2171
@ RTE_FUNCTION
Definition: parsenodes.h:1016
bool funcordinality
Definition: parsenodes.h:1148
List * functions
Definition: parsenodes.h:1147

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:560
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
@ RTE_CTE
Definition: parsenodes.h:1019
@ RTE_NAMEDTUPLESTORE
Definition: parsenodes.h:1020
@ RTE_TABLEFUNC
Definition: parsenodes.h:1017
@ RTE_RELATION
Definition: parsenodes.h:1013
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:1074
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:1199
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:211
void * palloc(Size size)
Definition: mcxt.c:1226
@ JOIN_FULL
Definition: nodes.h:306
@ JOIN_RIGHT
Definition: nodes.h:307
@ JOIN_LEFT
Definition: nodes.h:305
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:926
@ EXISTS_SUBLINK
Definition: primnodes.h:924
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:1375

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:121
@ JOIN_SEMI
Definition: nodes.h:318
@ JOIN_ANTI
Definition: nodes.h:319
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:2937

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  /* No need to wrap a PlaceHolderVar with another one, either */
2439  wrap = false;
2440  }
2441  else
2442  {
2443  /*
2444  * Must wrap, either because we need a place to insert
2445  * varnullingrels or because caller told us to wrap
2446  * everything.
2447  */
2448  wrap = true;
2449  }
2450 
2451  if (wrap)
2452  {
2453  newnode = (Node *)
2455  (Expr *) newnode,
2456  bms_make_singleton(rcon->varno));
2457 
2458  /*
2459  * Cache it if possible (ie, if the attno is in range, which
2460  * it probably always should be).
2461  */
2462  if (varattno > InvalidAttrNumber &&
2463  varattno <= list_length(rcon->targetlist))
2464  rcon->rv_cache[varattno] = copyObject(newnode);
2465  }
2466  }
2467  }
2468 
2469  /* Must adjust varlevelsup if replaced Var is within a subquery */
2470  if (var->varlevelsup > 0)
2471  IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
2472 
2473  /* Propagate any varnullingrels into the replacement Var or PHV */
2474  if (var->varnullingrels != NULL)
2475  {
2476  if (IsA(newnode, Var))
2477  {
2478  Var *newvar = (Var *) newnode;
2479 
2480  Assert(newvar->varlevelsup == var->varlevelsup);
2481  newvar->varnullingrels = bms_add_members(newvar->varnullingrels,
2482  var->varnullingrels);
2483  }
2484  else if (IsA(newnode, PlaceHolderVar))
2485  {
2486  PlaceHolderVar *newphv = (PlaceHolderVar *) newnode;
2487 
2488  Assert(newphv->phlevelsup == var->varlevelsup);
2489  newphv->phnullingrels = bms_add_members(newphv->phnullingrels,
2490  var->varnullingrels);
2491  }
2492  else
2493  elog(ERROR, "failed to wrap a non-Var");
2494  }
2495 
2496  return newnode;
2497 }
#define InvalidAttrNumber
Definition: attnum.h:23
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:460
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:835
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:663
Node * replace_rte_variables_mutator(Node *node, replace_rte_variables_context *context)
Relids phnullingrels
Definition: pathnodes.h:2753
int location
Definition: primnodes.h:1362
List * args
Definition: primnodes.h:1338
Expr * expr
Definition: primnodes.h:1895
Definition: primnodes.h:226
AttrNumber varattno
Definition: primnodes.h:238
int varno
Definition: primnodes.h:233
Index varlevelsup
Definition: primnodes.h:258
int location
Definition: primnodes.h:271

References RowExpr::args, Assert(), bms_add_members(), bms_is_member(), 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 2507 of file prepjointree.c.

2509 {
2510  Assert(IsA(query, Query));
2511  return (Query *) replace_rte_variables((Node *) query,
2512  context->varno, 1,
2514  (void *) context,
2515  NULL);
2516 }

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

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

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

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

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

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

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

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

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

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

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

3080 {
3082 
3083  statep = palloc(sizeof(reduce_outer_joins_partial_state));
3084  statep->full_join_rti = rtindex;
3085  statep->unreduced_side = relids;
3086  state2->partial_reduced = lappend(state2->partial_reduced, statep);
3087 }

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

3730 {
3732 
3733  context.varno = varno;
3734  context.sublevels_up = 0;
3735  context.subrelids = subrelids;
3736 
3737  /*
3738  * Must be prepared to start with a Query or a bare expression tree.
3739  */
3742  (void *) &context,
3743  0);
3744 }
#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 3685 of file prepjointree.c.

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

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:280
Node * quals
Definition: primnodes.h:1994
JoinType jointype
Definition: primnodes.h:1985
int rtindex
Definition: primnodes.h:1998
Node * larg
Definition: primnodes.h:1987
bool isNatural
Definition: primnodes.h:1986
Node * rarg
Definition: primnodes.h:1988
Alias * join_using_alias
Definition: parsenodes.h:1137
List * joinrightcols
Definition: parsenodes.h:1130
Alias * alias
Definition: parsenodes.h:1198
List * joinleftcols
Definition: parsenodes.h:1129

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