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

3584 {
3586 
3587  /* If there are no PHVs anywhere, we needn't work hard */
3588  if (root->glob->lastPHId == 0)
3589  return false;
3590 
3591  context.relids = bms_make_singleton(varno);
3592  context.sublevels_up = 0;
3593 
3594  if (query_tree_walker(root->parse,
3596  (void *) &context,
3597  0))
3598  return true;
3599  /* The append_rel_list could be populated already, so check it too */
3602  (void *) &context))
3603  return true;
3604  return false;
3605 }
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:186
#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:141
List * append_rel_list
Definition: pathnodes.h:365
PlannerGlobal * glob
Definition: pathnodes.h:205
Query * parse
Definition: pathnodes.h:202

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

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

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

3549 {
3550  if (node == NULL)
3551  return false;
3552  if (IsA(node, PlaceHolderVar))
3553  {
3554  PlaceHolderVar *phv = (PlaceHolderVar *) node;
3555 
3556  if (phv->phlevelsup == context->sublevels_up &&
3557  bms_equal(context->relids, phv->phrels))
3558  return true;
3559  /* fall through to examine children */
3560  }
3561  if (IsA(node, Query))
3562  {
3563  /* Recurse into subselects */
3564  bool result;
3565 
3566  context->sublevels_up++;
3567  result = query_tree_walker((Query *) node,
3569  (void *) context, 0);
3570  context->sublevels_up--;
3571  return result;
3572  }
3573  /* Shouldn't need to handle most planner auxiliary nodes here */
3574  Assert(!IsA(node, SpecialJoinInfo));
3575  Assert(!IsA(node, PlaceHolderInfo));
3576  Assert(!IsA(node, MinMaxAggInfo));
3577 
3579  (void *) context);
3580 }
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:94
Assert(fmt[strlen(fmt) - 1] !='\n')
#define IsA(nodeptr, _type_)
Definition: nodes.h:179
Index phlevelsup
Definition: pathnodes.h:2740

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

3867 {
3868  if (jtnode == NULL)
3869  return NULL;
3870  if (IsA(jtnode, RangeTblRef))
3871  {
3872  int varno = ((RangeTblRef *) jtnode)->rtindex;
3873 
3874  if (relid == varno)
3875  return jtnode;
3876  }
3877  else if (IsA(jtnode, FromExpr))
3878  {
3879  FromExpr *f = (FromExpr *) jtnode;
3880  ListCell *l;
3881 
3882  foreach(l, f->fromlist)
3883  {
3884  jtnode = find_jointree_node_for_rel(lfirst(l), relid);
3885  if (jtnode)
3886  return jtnode;
3887  }
3888  }
3889  else if (IsA(jtnode, JoinExpr))
3890  {
3891  JoinExpr *j = (JoinExpr *) jtnode;
3892 
3893  if (relid == j->rtindex)
3894  return jtnode;
3895  jtnode = find_jointree_node_for_rel(j->larg, relid);
3896  if (jtnode)
3897  return jtnode;
3898  jtnode = find_jointree_node_for_rel(j->rarg, relid);
3899  if (jtnode)
3900  return jtnode;
3901  }
3902  else
3903  elog(ERROR, "unrecognized node type: %d",
3904  (int) nodeTag(jtnode));
3905  return NULL;
3906 }
#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:1849

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

3745 {
3746  ListCell *l;
3747  int subvarno = -1;
3748 
3749  /*
3750  * We only want to extract the member relid once, but we mustn't fail
3751  * immediately if there are multiple members; it could be that none of the
3752  * AppendRelInfo nodes refer to it. So compute it on first use. Note that
3753  * bms_singleton_member will complain if set is not singleton.
3754  */
3755  foreach(l, root->append_rel_list)
3756  {
3757  AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
3758 
3759  /* The parent_relid shouldn't ever be a pullup target */
3760  Assert(appinfo->parent_relid != varno);
3761 
3762  if (appinfo->child_relid == varno)
3763  {
3764  if (subvarno < 0)
3765  subvarno = bms_singleton_member(subrelids);
3766  appinfo->child_relid = subvarno;
3767  }
3768 
3769  /* Also fix up any PHVs in its translated vars */
3770  if (root->glob->lastPHId != 0)
3772  varno, subrelids);
3773  }
3774 }
int bms_singleton_member(const Bitmapset *a)
Definition: bitmapset.c:596
static void substitute_phv_relids(Node *node, int varno, Relids subrelids)
Index child_relid
Definition: pathnodes.h:2904
List * translated_vars
Definition: pathnodes.h:2931
Index parent_relid
Definition: pathnodes.h:2903

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:1015
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, Query *setOpQuery, int childRToffset)
static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes)
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:717
bool hasRecursion
Definition: pathnodes.h:504
RTEKind rtekind
Definition: parsenodes.h:1033

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

3850 {
3851  Node *jtnode;
3852 
3853  jtnode = find_jointree_node_for_rel((Node *) query->jointree,
3854  joinrelid);
3855  if (!jtnode)
3856  elog(ERROR, "could not find join node %d", joinrelid);
3857  return get_relids_in_jointree(jtnode, true, false);
3858 }
FromExpr * jointree
Definition: parsenodes.h:182

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

3790 {
3791  Relids result = NULL;
3792 
3793  if (jtnode == NULL)
3794  return result;
3795  if (IsA(jtnode, RangeTblRef))
3796  {
3797  int varno = ((RangeTblRef *) jtnode)->rtindex;
3798 
3799  result = bms_make_singleton(varno);
3800  }
3801  else if (IsA(jtnode, FromExpr))
3802  {
3803  FromExpr *f = (FromExpr *) jtnode;
3804  ListCell *l;
3805 
3806  foreach(l, f->fromlist)
3807  {
3808  result = bms_join(result,
3810  include_outer_joins,
3811  include_inner_joins));
3812  }
3813  }
3814  else if (IsA(jtnode, JoinExpr))
3815  {
3816  JoinExpr *j = (JoinExpr *) jtnode;
3817 
3818  result = get_relids_in_jointree(j->larg,
3819  include_outer_joins,
3820  include_inner_joins);
3821  result = bms_join(result,
3822  get_relids_in_jointree(j->rarg,
3823  include_outer_joins,
3824  include_inner_joins));
3825  if (j->rtindex)
3826  {
3827  if (j->jointype == JOIN_INNER)
3828  {
3829  if (include_inner_joins)
3830  result = bms_add_member(result, j->rtindex);
3831  }
3832  else
3833  {
3834  if (include_outer_joins)
3835  result = bms_add_member(result, j->rtindex);
3836  }
3837  }
3838  }
3839  else
3840  elog(ERROR, "unrecognized node type: %d",
3841  (int) nodeTag(jtnode));
3842  return result;
3843 }
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:987
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:755
@ 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 3478 of file prepjointree.c.

3479 {
3480  int varno;
3481 
3482  if (!IsA(jtnode, RangeTblRef))
3483  return 0;
3484  varno = ((RangeTblRef *) jtnode)->rtindex;
3485  if (rt_fetch(varno, root->parse->rtable)->rtekind != RTE_RESULT)
3486  return 0;
3487  return varno;
3488 }
@ RTE_RESULT
Definition: parsenodes.h:1022

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

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:332
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:448
@ 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:212
Node * setOperations
Definition: parsenodes.h:217
List * cteList
Definition: parsenodes.h:173
List * groupClause
Definition: parsenodes.h:198
Node * havingQual
Definition: parsenodes.h:203
Node * limitOffset
Definition: parsenodes.h:211
CmdType commandType
Definition: parsenodes.h:128
List * targetList
Definition: parsenodes.h:189
List * groupingSets
Definition: parsenodes.h:201
List * distinctClause
Definition: parsenodes.h:207
List * sortClause
Definition: parsenodes.h:209
bool security_barrier
Definition: parsenodes.h:1082
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:215

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:1828
void check_stack_depth(void)
Definition: postgres.c:3461
Query * subquery
Definition: parsenodes.h:1081
SetOperation op
Definition: parsenodes.h:1905
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:706
@ RTE_VALUES
Definition: parsenodes.h:1019
List * values_lists
Definition: parsenodes.h:1159

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:105
void * palloc0(Size size)
Definition: mcxt.c:1241
int num_child_cols
Definition: pathnodes.h:2939
Definition: pg_list.h:54
AttrNumber resno
Definition: primnodes.h:1733
Definition: regcomp.c:282

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

Referenced by pull_up_union_leaf_queries().

◆ perform_pullup_replace_vars()

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

Definition at line 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:1016
#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:1129

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:4901
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2134
@ RTE_FUNCTION
Definition: parsenodes.h:1017
bool funcordinality
Definition: parsenodes.h:1149
List * functions
Definition: parsenodes.h:1148

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:240
#define linitial_node(type, l)
Definition: pg_list.h:181
unsigned int Oid
Definition: postgres_ext.h:31
static void perform_pullup_replace_vars(PlannerInfo *root, pullup_replace_vars_context *rvcontext, AppendRelInfo *containing_appendrel)
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:1020
@ RTE_NAMEDTUPLESTORE
Definition: parsenodes.h:1021
@ RTE_TABLEFUNC
Definition: parsenodes.h:1018
@ RTE_RELATION
Definition: parsenodes.h:1014
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:836
List * minmax_aggs
Definition: pathnodes.h:472
List * processed_tlist
Definition: pathnodes.h:456
List * cte_plan_ids
Definition: pathnodes.h:305
int last_rinfo_serial
Definition: pathnodes.h:343
Index qual_security_level
Definition: pathnodes.h:489
List * init_plans
Definition: pathnodes.h:299
List * multiexpr_params
Definition: pathnodes.h:308
List * row_identity_vars
Definition: pathnodes.h:368
bool ec_merging_done
Definition: pathnodes.h:317
Bitmapset * outer_params
Definition: pathnodes.h:221
Index query_level
Definition: pathnodes.h:208
struct Path * non_recursive_path
Definition: pathnodes.h:526
List * placeholder_list
Definition: pathnodes.h:374
List * join_domains
Definition: pathnodes.h:311
List * eq_classes
Definition: pathnodes.h:314
int wt_param_id
Definition: pathnodes.h:524
List * plan_params
Definition: pathnodes.h:220
List * processed_groupClause
Definition: pathnodes.h:433
List * processed_distinctClause
Definition: pathnodes.h:445
List * rowMarks
Definition: pathnodes.h:371
List * update_colnos
Definition: pathnodes.h:464
bool placeholdersFrozen
Definition: pathnodes.h:502
List * join_info_list
Definition: pathnodes.h:340
Relids all_result_relids
Definition: pathnodes.h:354
Relids leaf_result_relids
Definition: pathnodes.h:356
struct TableSampleClause * tablesample
Definition: parsenodes.h:1075
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:859

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:389
Alias * eref
Definition: parsenodes.h:1200
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:287
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 or JOIN_ANTI
410  * nodes 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:226
void * palloc(Size size)
Definition: mcxt.c:1210
@ 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:638
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:1268
JoinExpr * convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, bool under_not, Relids available_rels)
Definition: subselect.c:1376

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

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:444
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:818
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:2734
int location
Definition: primnodes.h:1360
List * args
Definition: primnodes.h:1336
Expr * expr
Definition: primnodes.h:1731
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 2652 of file prepjointree.c.

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

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

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

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

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

3509 {
3510  /* Fix up PlaceHolderVars as needed */
3511  /* If there are no PHVs anywhere, we can skip this bit */
3512  if (root->glob->lastPHId != 0)
3513  {
3514  Relids subrelids;
3515 
3516  subrelids = get_relids_in_jointree(newjtloc, true, false);
3517  Assert(!bms_is_empty(subrelids));
3518  substitute_phv_relids((Node *) root->parse, varno, subrelids);
3519  fix_append_rel_relids(root, varno, subrelids);
3520  }
3521 
3522  /*
3523  * We also need to remove any PlanRowMark referencing the RTE, but we
3524  * postpone that work until we return to remove_useless_result_rtes.
3525  */
3526 }

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

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

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

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

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

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

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

3717 {
3719 
3720  context.varno = varno;
3721  context.sublevels_up = 0;
3722  context.subrelids = subrelids;
3723 
3724  /*
3725  * Must be prepared to start with a Query or a bare expression tree.
3726  */
3729  (void *) &context,
3730  0);
3731 }
#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 3672 of file prepjointree.c.

3674 {
3675  if (node == NULL)
3676  return false;
3677  if (IsA(node, PlaceHolderVar))
3678  {
3679  PlaceHolderVar *phv = (PlaceHolderVar *) node;
3680 
3681  if (phv->phlevelsup == context->sublevels_up &&
3682  bms_is_member(context->varno, phv->phrels))
3683  {
3684  phv->phrels = bms_union(phv->phrels,
3685  context->subrelids);
3686  phv->phrels = bms_del_member(phv->phrels,
3687  context->varno);
3688  /* Assert we haven't broken the PHV */
3689  Assert(!bms_is_empty(phv->phrels));
3690  }
3691  /* fall through to examine children */
3692  }
3693  if (IsA(node, Query))
3694  {
3695  /* Recurse into subselects */
3696  bool result;
3697 
3698  context->sublevels_up++;
3699  result = query_tree_walker((Query *) node,
3701  (void *) context, 0);
3702  context->sublevels_up--;
3703  return result;
3704  }
3705  /* Shouldn't need to handle planner auxiliary nodes here */
3706  Assert(!IsA(node, SpecialJoinInfo));
3707  Assert(!IsA(node, AppendRelInfo));
3708  Assert(!IsA(node, PlaceHolderInfo));
3709  Assert(!IsA(node, MinMaxAggInfo));
3710 
3712  (void *) context);
3713 }
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:792

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:1830
JoinType jointype
Definition: primnodes.h:1821
int rtindex
Definition: primnodes.h:1834
Node * larg
Definition: primnodes.h:1823
bool isNatural
Definition: primnodes.h:1822
Node * rarg
Definition: primnodes.h:1824
Alias * join_using_alias
Definition: parsenodes.h:1138
List * joinrightcols
Definition: parsenodes.h:1131
Alias * alias
Definition: parsenodes.h:1199
List * joinleftcols
Definition: parsenodes.h:1130

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