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  nullingrel_info
 
struct  pullup_replace_vars_context
 
struct  reduce_outer_joins_pass1_state
 
struct  reduce_outer_joins_pass2_state
 
struct  reduce_outer_joins_partial_state
 
struct  find_dependent_phvs_context
 
struct  substitute_phv_relids_context
 

Typedefs

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

Functions

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

Typedef Documentation

◆ nullingrel_info

◆ pullup_replace_vars_context

◆ reduce_outer_joins_partial_state

◆ reduce_outer_joins_pass1_state

◆ reduce_outer_joins_pass2_state

Function Documentation

◆ find_dependent_phvs()

static bool find_dependent_phvs ( PlannerInfo root,
int  varno 
)
static

Definition at line 3931 of file prepjointree.c.

3932{
3934
3935 /* If there are no PHVs anywhere, we needn't work hard */
3936 if (root->glob->lastPHId == 0)
3937 return false;
3938
3939 context.relids = bms_make_singleton(varno);
3940 context.sublevels_up = 0;
3941
3942 if (query_tree_walker(root->parse, find_dependent_phvs_walker, &context, 0))
3943 return true;
3944 /* The append_rel_list could be populated already, so check it too */
3945 if (expression_tree_walker((Node *) root->append_rel_list,
3947 &context))
3948 return true;
3949 return false;
3950}
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
#define query_tree_walker(q, w, c, f)
Definition: nodeFuncs.h:158
#define expression_tree_walker(n, w, c)
Definition: nodeFuncs.h:153
static bool find_dependent_phvs_walker(Node *node, find_dependent_phvs_context *context)
tree ctl root
Definition: radixtree.h:1857
Definition: nodes.h:129

References bms_make_singleton(), expression_tree_walker, find_dependent_phvs_walker(), query_tree_walker, find_dependent_phvs_context::relids, root, 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 3953 of file prepjointree.c.

3954{
3956 Relids subrelids;
3957 int relid;
3958
3959 /* If there are no PHVs anywhere, we needn't work hard */
3960 if (root->glob->lastPHId == 0)
3961 return false;
3962
3963 context.relids = bms_make_singleton(varno);
3964 context.sublevels_up = 0;
3965
3966 /*
3967 * See if the jointree fragment itself contains references (in join quals)
3968 */
3969 if (find_dependent_phvs_walker(node, &context))
3970 return true;
3971
3972 /*
3973 * Otherwise, identify the set of referenced RTEs (we can ignore joins,
3974 * since they should be flattened already, so their join alias lists no
3975 * longer matter), and tediously check each RTE. We can ignore RTEs that
3976 * are not marked LATERAL, though, since they couldn't possibly contain
3977 * any cross-references to other RTEs.
3978 */
3979 subrelids = get_relids_in_jointree(node, false, false);
3980 relid = -1;
3981 while ((relid = bms_next_member(subrelids, relid)) >= 0)
3982 {
3983 RangeTblEntry *rte = rt_fetch(relid, root->parse->rtable);
3984
3985 if (rte->lateral &&
3987 return true;
3988 }
3989
3990 return false;
3991}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
#define range_table_entry_walker(r, w, c, f)
Definition: nodeFuncs.h:168
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
Relids get_relids_in_jointree(Node *jtnode, bool include_outer_joins, bool include_inner_joins)

References bms_make_singleton(), bms_next_member(), find_dependent_phvs_walker(), get_relids_in_jointree(), range_table_entry_walker, find_dependent_phvs_context::relids, root, rt_fetch, 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 3896 of file prepjointree.c.

3898{
3899 if (node == NULL)
3900 return false;
3901 if (IsA(node, PlaceHolderVar))
3902 {
3903 PlaceHolderVar *phv = (PlaceHolderVar *) node;
3904
3905 if (phv->phlevelsup == context->sublevels_up &&
3906 bms_equal(context->relids, phv->phrels))
3907 return true;
3908 /* fall through to examine children */
3909 }
3910 if (IsA(node, Query))
3911 {
3912 /* Recurse into subselects */
3913 bool result;
3914
3915 context->sublevels_up++;
3916 result = query_tree_walker((Query *) node,
3918 context, 0);
3919 context->sublevels_up--;
3920 return result;
3921 }
3922 /* Shouldn't need to handle most planner auxiliary nodes here */
3923 Assert(!IsA(node, SpecialJoinInfo));
3924 Assert(!IsA(node, PlaceHolderInfo));
3925 Assert(!IsA(node, MinMaxAggInfo));
3926
3928}
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:142
#define Assert(condition)
Definition: c.h:815
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
Index phlevelsup
Definition: pathnodes.h:2827

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

Referenced by find_dependent_phvs(), find_dependent_phvs_in_jointree(), and find_dependent_phvs_walker().

◆ find_jointree_node_for_rel()

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

Definition at line 4207 of file prepjointree.c.

4208{
4209 if (jtnode == NULL)
4210 return NULL;
4211 if (IsA(jtnode, RangeTblRef))
4212 {
4213 int varno = ((RangeTblRef *) jtnode)->rtindex;
4214
4215 if (relid == varno)
4216 return jtnode;
4217 }
4218 else if (IsA(jtnode, FromExpr))
4219 {
4220 FromExpr *f = (FromExpr *) jtnode;
4221 ListCell *l;
4222
4223 foreach(l, f->fromlist)
4224 {
4225 jtnode = find_jointree_node_for_rel(lfirst(l), relid);
4226 if (jtnode)
4227 return jtnode;
4228 }
4229 }
4230 else if (IsA(jtnode, JoinExpr))
4231 {
4232 JoinExpr *j = (JoinExpr *) jtnode;
4233
4234 if (relid == j->rtindex)
4235 return jtnode;
4236 jtnode = find_jointree_node_for_rel(j->larg, relid);
4237 if (jtnode)
4238 return jtnode;
4239 jtnode = find_jointree_node_for_rel(j->rarg, relid);
4240 if (jtnode)
4241 return jtnode;
4242 }
4243 else
4244 elog(ERROR, "unrecognized node type: %d",
4245 (int) nodeTag(jtnode));
4246 return NULL;
4247}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
int j
Definition: isn.c:73
#define nodeTag(nodeptr)
Definition: nodes.h:133
#define lfirst(lc)
Definition: pg_list.h:172
static Node * find_jointree_node_for_rel(Node *jtnode, int relid)
List * fromlist
Definition: primnodes.h:2337

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

Referenced by find_jointree_node_for_rel(), and get_relids_for_join().

◆ fix_append_rel_relids()

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

Definition at line 4085 of file prepjointree.c.

4086{
4087 ListCell *l;
4088 int subvarno = -1;
4089
4090 /*
4091 * We only want to extract the member relid once, but we mustn't fail
4092 * immediately if there are multiple members; it could be that none of the
4093 * AppendRelInfo nodes refer to it. So compute it on first use. Note that
4094 * bms_singleton_member will complain if set is not singleton.
4095 */
4096 foreach(l, root->append_rel_list)
4097 {
4098 AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
4099
4100 /* The parent_relid shouldn't ever be a pullup target */
4101 Assert(appinfo->parent_relid != varno);
4102
4103 if (appinfo->child_relid == varno)
4104 {
4105 if (subvarno < 0)
4106 subvarno = bms_singleton_member(subrelids);
4107 appinfo->child_relid = subvarno;
4108 }
4109
4110 /* Also fix up any PHVs in its translated vars */
4111 if (root->glob->lastPHId != 0)
4113 varno, subrelids);
4114 }
4115}
int bms_singleton_member(const Bitmapset *a)
Definition: bitmapset.c:672
static void substitute_phv_relids(Node *node, int varno, Relids subrelids)
Index child_relid
Definition: pathnodes.h:3000
List * translated_vars
Definition: pathnodes.h:3027
Index parent_relid
Definition: pathnodes.h:2999

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

Referenced by pull_up_simple_subquery(), and remove_result_refs().

◆ flatten_simple_union_all()

void flatten_simple_union_all ( PlannerInfo root)

Definition at line 2870 of file prepjointree.c.

2871{
2872 Query *parse = root->parse;
2873 SetOperationStmt *topop;
2874 Node *leftmostjtnode;
2875 int leftmostRTI;
2876 RangeTblEntry *leftmostRTE;
2877 int childRTI;
2878 RangeTblEntry *childRTE;
2879 RangeTblRef *rtr;
2880
2881 /* Shouldn't be called unless query has setops */
2882 topop = castNode(SetOperationStmt, parse->setOperations);
2883 Assert(topop);
2884
2885 /* Can't optimize away a recursive UNION */
2886 if (root->hasRecursion)
2887 return;
2888
2889 /*
2890 * Recursively check the tree of set operations. If not all UNION ALL
2891 * with identical column types, punt.
2892 */
2893 if (!is_simple_union_all_recurse((Node *) topop, parse, topop->colTypes))
2894 return;
2895
2896 /*
2897 * Locate the leftmost leaf query in the setops tree. The upper query's
2898 * Vars all refer to this RTE (see transformSetOperationStmt).
2899 */
2900 leftmostjtnode = topop->larg;
2901 while (leftmostjtnode && IsA(leftmostjtnode, SetOperationStmt))
2902 leftmostjtnode = ((SetOperationStmt *) leftmostjtnode)->larg;
2903 Assert(leftmostjtnode && IsA(leftmostjtnode, RangeTblRef));
2904 leftmostRTI = ((RangeTblRef *) leftmostjtnode)->rtindex;
2905 leftmostRTE = rt_fetch(leftmostRTI, parse->rtable);
2906 Assert(leftmostRTE->rtekind == RTE_SUBQUERY);
2907
2908 /*
2909 * Make a copy of the leftmost RTE and add it to the rtable. This copy
2910 * will represent the leftmost leaf query in its capacity as a member of
2911 * the appendrel. The original will represent the appendrel as a whole.
2912 * (We must do things this way because the upper query's Vars have to be
2913 * seen as referring to the whole appendrel.)
2914 */
2915 childRTE = copyObject(leftmostRTE);
2916 parse->rtable = lappend(parse->rtable, childRTE);
2917 childRTI = list_length(parse->rtable);
2918
2919 /* Modify the setops tree to reference the child copy */
2920 ((RangeTblRef *) leftmostjtnode)->rtindex = childRTI;
2921
2922 /* Modify the formerly-leftmost RTE to mark it as an appendrel parent */
2923 leftmostRTE->inh = true;
2924
2925 /*
2926 * Form a RangeTblRef for the appendrel, and insert it into FROM. The top
2927 * Query of a setops tree should have had an empty FromClause initially.
2928 */
2929 rtr = makeNode(RangeTblRef);
2930 rtr->rtindex = leftmostRTI;
2931 Assert(parse->jointree->fromlist == NIL);
2932 parse->jointree->fromlist = list_make1(rtr);
2933
2934 /*
2935 * Now pretend the query has no setops. We must do this before trying to
2936 * do subquery pullup, because of Assert in pull_up_simple_subquery.
2937 */
2938 parse->setOperations = NULL;
2939
2940 /*
2941 * Build AppendRelInfo information, and apply pull_up_subqueries to the
2942 * leaf queries of the UNION ALL. (We must do that now because they
2943 * weren't previously referenced by the jointree, and so were missed by
2944 * the main invocation of pull_up_subqueries.)
2945 */
2946 pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0);
2947}
List * lappend(List *list, void *datum)
Definition: list.c:339
#define copyObject(obj)
Definition: nodes.h:224
#define makeNode(_type_)
Definition: nodes.h:155
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
@ RTE_SUBQUERY
Definition: parsenodes.h:1027
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, Query *setOpQuery, int childRToffset)
static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes)
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:717
RTEKind rtekind
Definition: parsenodes.h:1056

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

Referenced by subquery_planner().

◆ get_nullingrels()

static nullingrel_info * get_nullingrels ( Query parse)
static

Definition at line 4256 of file prepjointree.c.

4257{
4259
4260 result->rtlength = list_length(parse->rtable);
4261 result->nullingrels = palloc0_array(Relids, result->rtlength + 1);
4262 get_nullingrels_recurse((Node *) parse->jointree, NULL, result);
4263 return result;
4264}
#define palloc_object(type)
Definition: fe_memutils.h:74
#define palloc0_array(type, count)
Definition: fe_memutils.h:77
static void get_nullingrels_recurse(Node *jtnode, Relids upper_nullingrels, nullingrel_info *info)
Relids * nullingrels
Definition: prepjointree.c:51

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

Referenced by pull_up_simple_subquery().

◆ get_nullingrels_recurse()

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

Definition at line 4276 of file prepjointree.c.

4278{
4279 if (jtnode == NULL)
4280 return;
4281 if (IsA(jtnode, RangeTblRef))
4282 {
4283 int varno = ((RangeTblRef *) jtnode)->rtindex;
4284
4285 Assert(varno > 0 && varno <= info->rtlength);
4286 info->nullingrels[varno] = upper_nullingrels;
4287 }
4288 else if (IsA(jtnode, FromExpr))
4289 {
4290 FromExpr *f = (FromExpr *) jtnode;
4291 ListCell *l;
4292
4293 foreach(l, f->fromlist)
4294 {
4295 get_nullingrels_recurse(lfirst(l), upper_nullingrels, info);
4296 }
4297 }
4298 else if (IsA(jtnode, JoinExpr))
4299 {
4300 JoinExpr *j = (JoinExpr *) jtnode;
4301 Relids local_nullingrels;
4302
4303 switch (j->jointype)
4304 {
4305 case JOIN_INNER:
4306 get_nullingrels_recurse(j->larg, upper_nullingrels, info);
4307 get_nullingrels_recurse(j->rarg, upper_nullingrels, info);
4308 break;
4309 case JOIN_LEFT:
4310 case JOIN_SEMI:
4311 case JOIN_ANTI:
4312 local_nullingrels = bms_add_member(bms_copy(upper_nullingrels),
4313 j->rtindex);
4314 get_nullingrels_recurse(j->larg, upper_nullingrels, info);
4315 get_nullingrels_recurse(j->rarg, local_nullingrels, info);
4316 break;
4317 case JOIN_FULL:
4318 local_nullingrels = bms_add_member(bms_copy(upper_nullingrels),
4319 j->rtindex);
4320 get_nullingrels_recurse(j->larg, local_nullingrels, info);
4321 get_nullingrels_recurse(j->rarg, local_nullingrels, info);
4322 break;
4323 case JOIN_RIGHT:
4324 local_nullingrels = bms_add_member(bms_copy(upper_nullingrels),
4325 j->rtindex);
4326 get_nullingrels_recurse(j->larg, local_nullingrels, info);
4327 get_nullingrels_recurse(j->rarg, upper_nullingrels, info);
4328 break;
4329 default:
4330 elog(ERROR, "unrecognized join type: %d",
4331 (int) j->jointype);
4332 break;
4333 }
4334 }
4335 else
4336 elog(ERROR, "unrecognized node type: %d",
4337 (int) nodeTag(jtnode));
4338}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:122
@ JOIN_SEMI
Definition: nodes.h:307
@ JOIN_FULL
Definition: nodes.h:295
@ JOIN_INNER
Definition: nodes.h:293
@ JOIN_RIGHT
Definition: nodes.h:296
@ JOIN_LEFT
Definition: nodes.h:294
@ JOIN_ANTI
Definition: nodes.h:308

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

Referenced by get_nullingrels(), and get_nullingrels_recurse().

◆ get_relids_for_join()

Relids get_relids_for_join ( Query query,
int  joinrelid 
)

Definition at line 4190 of file prepjointree.c.

4191{
4192 Node *jtnode;
4193
4194 jtnode = find_jointree_node_for_rel((Node *) query->jointree,
4195 joinrelid);
4196 if (!jtnode)
4197 elog(ERROR, "could not find join node %d", joinrelid);
4198 return get_relids_in_jointree(jtnode, true, false);
4199}
FromExpr * jointree
Definition: parsenodes.h:177

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

Referenced by add_nullingrels_if_needed(), and alias_relid_set().

◆ get_relids_in_jointree()

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

Definition at line 4129 of file prepjointree.c.

4131{
4132 Relids result = NULL;
4133
4134 if (jtnode == NULL)
4135 return result;
4136 if (IsA(jtnode, RangeTblRef))
4137 {
4138 int varno = ((RangeTblRef *) jtnode)->rtindex;
4139
4140 result = bms_make_singleton(varno);
4141 }
4142 else if (IsA(jtnode, FromExpr))
4143 {
4144 FromExpr *f = (FromExpr *) jtnode;
4145 ListCell *l;
4146
4147 foreach(l, f->fromlist)
4148 {
4149 result = bms_join(result,
4151 include_outer_joins,
4152 include_inner_joins));
4153 }
4154 }
4155 else if (IsA(jtnode, JoinExpr))
4156 {
4157 JoinExpr *j = (JoinExpr *) jtnode;
4158
4159 result = get_relids_in_jointree(j->larg,
4160 include_outer_joins,
4161 include_inner_joins);
4162 result = bms_join(result,
4164 include_outer_joins,
4165 include_inner_joins));
4166 if (j->rtindex)
4167 {
4168 if (j->jointype == JOIN_INNER)
4169 {
4170 if (include_inner_joins)
4171 result = bms_add_member(result, j->rtindex);
4172 }
4173 else
4174 {
4175 if (include_outer_joins)
4176 result = bms_add_member(result, j->rtindex);
4177 }
4178 }
4179 }
4180 else
4181 elog(ERROR, "unrecognized node type: %d",
4182 (int) nodeTag(jtnode));
4183 return result;
4184}
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:1230

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

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

◆ get_result_relid()

static int get_result_relid ( PlannerInfo root,
Node jtnode 
)
static

Definition at line 3827 of file prepjointree.c.

3828{
3829 int varno;
3830
3831 if (!IsA(jtnode, RangeTblRef))
3832 return 0;
3833 varno = ((RangeTblRef *) jtnode)->rtindex;
3834 if (rt_fetch(varno, root->parse->rtable)->rtekind != RTE_RESULT)
3835 return 0;
3836 return varno;
3837}
@ RTE_RESULT
Definition: parsenodes.h:1034

References IsA, root, rt_fetch, and RTE_RESULT.

Referenced by remove_useless_results_recurse().

◆ is_safe_append_member()

static bool is_safe_append_member ( Query subquery)
static

Definition at line 2138 of file prepjointree.c.

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

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

Referenced by pull_up_simple_subquery(), and pull_up_subqueries_recurse().

◆ is_simple_subquery()

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

Definition at line 1654 of file prepjointree.c.

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

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

Referenced by pull_up_simple_subquery(), and pull_up_subqueries_recurse().

◆ is_simple_union_all()

static bool is_simple_union_all ( Query subquery)
static

Definition at line 2067 of file prepjointree.c.

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

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

Referenced by pull_up_subqueries_recurse().

◆ is_simple_union_all_recurse()

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

Definition at line 2095 of file prepjointree.c.

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

References SetOperationStmt::all, Assert, check_stack_depth(), elog, ERROR, is_simple_union_all_recurse(), 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(), is_simple_union_all(), and is_simple_union_all_recurse().

◆ is_simple_values()

static bool is_simple_values ( PlannerInfo root,
RangeTblEntry rte 
)
static

Definition at line 1890 of file prepjointree.c.

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

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

Referenced by pull_up_subqueries_recurse().

◆ jointree_contains_lateral_outer_refs()

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

Definition at line 2186 of file prepjointree.c.

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

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

Referenced by is_simple_subquery(), and jointree_contains_lateral_outer_refs().

◆ make_setop_translation_list()

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

Definition at line 1616 of file prepjointree.c.

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

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

Referenced by pull_up_union_leaf_queries().

◆ perform_pullup_replace_vars()

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

Definition at line 2261 of file prepjointree.c.

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

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

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

◆ preprocess_function_rtes()

void preprocess_function_rtes ( PlannerInfo root)

Definition at line 887 of file prepjointree.c.

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

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

Referenced by pull_up_simple_subquery(), and subquery_planner().

◆ pull_up_constant_function()

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

Definition at line 1949 of file prepjointree.c.

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

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

Referenced by pull_up_subqueries_recurse().

◆ pull_up_simple_subquery()

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

Definition at line 1117 of file prepjointree.c.

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

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

Referenced by pull_up_subqueries_recurse().

◆ pull_up_simple_union_all()

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

Definition at line 1464 of file prepjointree.c.

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

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

Referenced by pull_up_subqueries_recurse().

◆ pull_up_simple_values()

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

Definition at line 1794 of file prepjointree.c.

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

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

Referenced by pull_up_subqueries_recurse().

◆ pull_up_sublinks()

void pull_up_sublinks ( PlannerInfo root)

Definition at line 453 of file prepjointree.c.

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

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

Referenced by pull_up_simple_subquery(), and subquery_planner().

◆ pull_up_sublinks_jointree_recurse()

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

Definition at line 480 of file prepjointree.c.

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

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

Referenced by pull_up_sublinks(), pull_up_sublinks_jointree_recurse(), and pull_up_sublinks_qual_recurse().

◆ pull_up_sublinks_qual_recurse()

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

Definition at line 637 of file prepjointree.c.

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

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

Referenced by pull_up_sublinks_jointree_recurse(), and pull_up_sublinks_qual_recurse().

◆ pull_up_subqueries()

void pull_up_subqueries ( PlannerInfo root)

Definition at line 928 of file prepjointree.c.

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

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

Referenced by pull_up_simple_subquery(), and subquery_planner().

◆ pull_up_subqueries_recurse()

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

Definition at line 972 of file prepjointree.c.

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

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

Referenced by pull_up_subqueries(), pull_up_subqueries_recurse(), and pull_up_union_leaf_queries().

◆ pull_up_union_leaf_queries()

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

Definition at line 1546 of file prepjointree.c.

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

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

Referenced by flatten_simple_union_all(), pull_up_simple_union_all(), and pull_up_union_leaf_queries().

◆ pullup_replace_vars()

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

Definition at line 2475 of file prepjointree.c.

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

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

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

References add_nulling_relids(), RowExpr::args, Assert, bms_add_members(), bms_del_members(), bms_intersect(), bms_is_empty, bms_is_member(), bms_is_subset(), bms_make_singleton(), bms_next_member(), bms_overlap(), replace_rte_variables_context::callback_arg, COERCE_IMPLICIT_CAST, contain_nonstrict_functions(), contain_vars_of_level(), copyObject, elog, ERROR, expandRTE(), TargetEntry::expr, get_tle_by_resno(), IncrementVarSublevelsUp(), InvalidAttrNumber, IsA, list_length(), Var::location, RowExpr::location, make_placeholder_expr(), makeNode, NIL, pullup_replace_vars_context::nullinfo, nullingrel_info::nullingrels, PlaceHolderVar::phlevelsup, PlaceHolderVar::phnullingrels, pull_varnos(), pullup_replace_vars_context::relids, replace_rte_variables_mutator(), pullup_replace_vars_context::root, pullup_replace_vars_context::rv_cache, 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, Var::varreturningtype, 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 2843 of file prepjointree.c.

2845{
2846 Assert(IsA(query, Query));
2847 return (Query *) replace_rte_variables((Node *) query,
2848 context->varno, 1,
2850 context,
2851 NULL);
2852}

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

2990{
2993 ListCell *lc;
2994
2995 /*
2996 * To avoid doing strictness checks on more quals than necessary, we want
2997 * to stop descending the jointree as soon as there are no outer joins
2998 * below our current point. This consideration forces a two-pass process.
2999 * The first pass gathers information about which base rels appear below
3000 * each side of each join clause, and about whether there are outer
3001 * join(s) below each side of each join clause. The second pass examines
3002 * qual clauses and changes join types as it descends the tree.
3003 */
3004 state1 = reduce_outer_joins_pass1((Node *) root->parse->jointree);
3005
3006 /* planner.c shouldn't have called me if no outer joins */
3007 if (state1 == NULL || !state1->contains_outer)
3008 elog(ERROR, "so where are the outer joins?");
3009
3010 state2.inner_reduced = NULL;
3011 state2.partial_reduced = NIL;
3012
3013 reduce_outer_joins_pass2((Node *) root->parse->jointree,
3014 state1, &state2,
3015 root, NULL, NIL);
3016
3017 /*
3018 * If we successfully reduced the strength of any outer joins, we must
3019 * remove references to those joins as nulling rels. This is handled as
3020 * an additional pass, for simplicity and because we can handle all
3021 * fully-reduced joins in a single pass over the parse tree.
3022 */
3023 if (!bms_is_empty(state2.inner_reduced))
3024 {
3025 root->parse = (Query *)
3026 remove_nulling_relids((Node *) root->parse,
3027 state2.inner_reduced,
3028 NULL);
3029 /* There could be references in the append_rel_list, too */
3030 root->append_rel_list = (List *)
3031 remove_nulling_relids((Node *) root->append_rel_list,
3032 state2.inner_reduced,
3033 NULL);
3034 }
3035
3036 /*
3037 * Partially-reduced full joins have to be done one at a time, since
3038 * they'll each need a different setting of except_relids.
3039 */
3040 foreach(lc, state2.partial_reduced)
3041 {
3043 Relids full_join_relids = bms_make_singleton(statep->full_join_rti);
3044
3045 root->parse = (Query *)
3046 remove_nulling_relids((Node *) root->parse,
3047 full_join_relids,
3048 statep->unreduced_side);
3049 root->append_rel_list = (List *)
3050 remove_nulling_relids((Node *) root->append_rel_list,
3051 full_join_relids,
3052 statep->unreduced_side);
3053 }
3054}
static void reduce_outer_joins_pass2(Node *jtnode, reduce_outer_joins_pass1_state *state1, reduce_outer_joins_pass2_state *state2, PlannerInfo *root, Relids nonnullable_rels, List *forced_null_vars)
static reduce_outer_joins_pass1_state * reduce_outer_joins_pass1(Node *jtnode)
Node * remove_nulling_relids(Node *node, const Bitmapset *removable_relids, const Bitmapset *except_relids)

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

Referenced by subquery_planner().

◆ reduce_outer_joins_pass1()

static reduce_outer_joins_pass1_state * reduce_outer_joins_pass1 ( Node jtnode)
static

Definition at line 3062 of file prepjointree.c.

3063{
3065
3068 result->relids = NULL;
3069 result->contains_outer = false;
3070 result->sub_states = NIL;
3071
3072 if (jtnode == NULL)
3073 return result;
3074 if (IsA(jtnode, RangeTblRef))
3075 {
3076 int varno = ((RangeTblRef *) jtnode)->rtindex;
3077
3078 result->relids = bms_make_singleton(varno);
3079 }
3080 else if (IsA(jtnode, FromExpr))
3081 {
3082 FromExpr *f = (FromExpr *) jtnode;
3083 ListCell *l;
3084
3085 foreach(l, f->fromlist)
3086 {
3088
3089 sub_state = reduce_outer_joins_pass1(lfirst(l));
3090 result->relids = bms_add_members(result->relids,
3091 sub_state->relids);
3092 result->contains_outer |= sub_state->contains_outer;
3093 result->sub_states = lappend(result->sub_states, sub_state);
3094 }
3095 }
3096 else if (IsA(jtnode, JoinExpr))
3097 {
3098 JoinExpr *j = (JoinExpr *) jtnode;
3100
3101 /* join's own RT index is not wanted in result->relids */
3102 if (IS_OUTER_JOIN(j->jointype))
3103 result->contains_outer = true;
3104
3105 sub_state = reduce_outer_joins_pass1(j->larg);
3106 result->relids = bms_add_members(result->relids,
3107 sub_state->relids);
3108 result->contains_outer |= sub_state->contains_outer;
3109 result->sub_states = lappend(result->sub_states, sub_state);
3110
3111 sub_state = reduce_outer_joins_pass1(j->rarg);
3112 result->relids = bms_add_members(result->relids,
3113 sub_state->relids);
3114 result->contains_outer |= sub_state->contains_outer;
3115 result->sub_states = lappend(result->sub_states, sub_state);
3116 }
3117 else
3118 elog(ERROR, "unrecognized node type: %d",
3119 (int) nodeTag(jtnode));
3120 return result;
3121}
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:338

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

Referenced by reduce_outer_joins(), and reduce_outer_joins_pass1().

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

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

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

Referenced by reduce_outer_joins(), and reduce_outer_joins_pass2().

◆ remove_result_refs()

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

Definition at line 3857 of file prepjointree.c.

3858{
3859 /* Fix up PlaceHolderVars as needed */
3860 /* If there are no PHVs anywhere, we can skip this bit */
3861 if (root->glob->lastPHId != 0)
3862 {
3863 Relids subrelids;
3864
3865 subrelids = get_relids_in_jointree(newjtloc, true, false);
3866 Assert(!bms_is_empty(subrelids));
3867 substitute_phv_relids((Node *) root->parse, varno, subrelids);
3868 fix_append_rel_relids(root, varno, subrelids);
3869 }
3870
3871 /*
3872 * We also need to remove any PlanRowMark referencing the RTE, but we
3873 * postpone that work until we return to remove_useless_result_rtes.
3874 */
3875}

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

Referenced by remove_useless_results_recurse().

◆ remove_useless_result_rtes()

void remove_useless_result_rtes ( PlannerInfo root)

Definition at line 3483 of file prepjointree.c.

3484{
3485 Relids dropped_outer_joins = NULL;
3486 ListCell *cell;
3487
3488 /* Top level of jointree must always be a FromExpr */
3489 Assert(IsA(root->parse->jointree, FromExpr));
3490 /* Recurse ... */
3491 root->parse->jointree = (FromExpr *)
3493 (Node *) root->parse->jointree,
3494 NULL,
3495 &dropped_outer_joins);
3496 /* We should still have a FromExpr */
3497 Assert(IsA(root->parse->jointree, FromExpr));
3498
3499 /*
3500 * If we removed any outer-join nodes from the jointree, run around and
3501 * remove references to those joins as nulling rels. (There could be such
3502 * references in PHVs that we pulled up out of the original subquery that
3503 * the RESULT rel replaced. This is kosher on the grounds that we now
3504 * know that such an outer join wouldn't really have nulled anything.) We
3505 * don't do this during the main recursion, for simplicity and because we
3506 * can handle all such joins in a single pass over the parse tree.
3507 */
3508 if (!bms_is_empty(dropped_outer_joins))
3509 {
3510 root->parse = (Query *)
3511 remove_nulling_relids((Node *) root->parse,
3512 dropped_outer_joins,
3513 NULL);
3514 /* There could be references in the append_rel_list, too */
3515 root->append_rel_list = (List *)
3516 remove_nulling_relids((Node *) root->append_rel_list,
3517 dropped_outer_joins,
3518 NULL);
3519 }
3520
3521 /*
3522 * Remove any PlanRowMark referencing an RTE_RESULT RTE. We obviously
3523 * must do that for any RTE_RESULT that we just removed. But one for a
3524 * RTE that we did not remove can be dropped anyway: since the RTE has
3525 * only one possible output row, there is no need for EPQ to mark and
3526 * restore that row.
3527 *
3528 * It's necessary, not optional, to remove the PlanRowMark for a surviving
3529 * RTE_RESULT RTE; otherwise we'll generate a whole-row Var for the
3530 * RTE_RESULT, which the executor has no support for.
3531 */
3532 foreach(cell, root->rowMarks)
3533 {
3534 PlanRowMark *rc = (PlanRowMark *) lfirst(cell);
3535
3536 if (rt_fetch(rc->rti, root->parse->rtable)->rtekind == RTE_RESULT)
3537 root->rowMarks = foreach_delete_current(root->rowMarks, cell);
3538 }
3539}
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
static Node * remove_useless_results_recurse(PlannerInfo *root, Node *jtnode, Node **parent_quals, Relids *dropped_outer_joins)

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

Referenced by subquery_planner().

◆ remove_useless_results_recurse()

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

Definition at line 3556 of file prepjointree.c.

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

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

Referenced by remove_useless_result_rtes(), and remove_useless_results_recurse().

◆ replace_empty_jointree()

void replace_empty_jointree ( Query parse)

Definition at line 395 of file prepjointree.c.

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

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

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

◆ replace_vars_in_jointree()

static void replace_vars_in_jointree ( Node jtnode,
pullup_replace_vars_context context 
)
static

Definition at line 2368 of file prepjointree.c.

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

References Assert, elog, ERROR, FromExpr::fromlist, RangeTblEntry::functions, IsA, j, JOIN_FULL, lfirst, nodeTag, PlannerInfo::parse, pullup_replace_vars(), pullup_replace_vars_subquery(), FromExpr::quals, replace_vars_in_jointree(), pullup_replace_vars_context::root, rt_fetch, Query::rtable, RTE_CTE, RTE_FUNCTION, RTE_GROUP, RTE_JOIN, RTE_NAMEDTUPLESTORE, RTE_RELATION, RTE_RESULT, RTE_SUBQUERY, RTE_TABLEFUNC, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::subquery, RangeTblEntry::tablefunc, RangeTblEntry::tablesample, 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(), and replace_vars_in_jointree().

◆ report_reduced_full_join()

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

◆ substitute_phv_relids()

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

Definition at line 4057 of file prepjointree.c.

4058{
4060
4061 context.varno = varno;
4062 context.sublevels_up = 0;
4063 context.subrelids = subrelids;
4064
4065 /*
4066 * Must be prepared to start with a Query or a bare expression tree.
4067 */
4070 &context,
4071 0);
4072}
#define query_or_expression_tree_walker(n, w, c, f)
Definition: nodeFuncs.h:171
static bool substitute_phv_relids_walker(Node *node, substitute_phv_relids_context *context)

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

4016{
4017 if (node == NULL)
4018 return false;
4019 if (IsA(node, PlaceHolderVar))
4020 {
4021 PlaceHolderVar *phv = (PlaceHolderVar *) node;
4022
4023 if (phv->phlevelsup == context->sublevels_up &&
4024 bms_is_member(context->varno, phv->phrels))
4025 {
4026 phv->phrels = bms_union(phv->phrels,
4027 context->subrelids);
4028 phv->phrels = bms_del_member(phv->phrels,
4029 context->varno);
4030 /* Assert we haven't broken the PHV */
4031 Assert(!bms_is_empty(phv->phrels));
4032 }
4033 /* fall through to examine children */
4034 }
4035 if (IsA(node, Query))
4036 {
4037 /* Recurse into subselects */
4038 bool result;
4039
4040 context->sublevels_up++;
4041 result = query_tree_walker((Query *) node,
4043 context, 0);
4044 context->sublevels_up--;
4045 return result;
4046 }
4047 /* Shouldn't need to handle planner auxiliary nodes here */
4048 Assert(!IsA(node, SpecialJoinInfo));
4049 Assert(!IsA(node, AppendRelInfo));
4050 Assert(!IsA(node, PlaceHolderInfo));
4051 Assert(!IsA(node, MinMaxAggInfo));
4052
4054}
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:868

References Assert, bms_del_member(), bms_is_empty, bms_is_member(), bms_union(), expression_tree_walker, IsA, PlaceHolderVar::phlevelsup, query_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 substitute_phv_relids(), and substitute_phv_relids_walker().

◆ transform_MERGE_to_join()

void transform_MERGE_to_join ( Query parse)

Definition at line 168 of file prepjointree.c.

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

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

Referenced by subquery_planner().