PostgreSQL Source Code git master
Loading...
Searching...
No Matches
prep.h File Reference
#include "nodes/pathnodes.h"
#include "nodes/plannodes.h"
Include dependency graph for prep.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

void transform_MERGE_to_join (Query *parse)
 
Querypreprocess_relation_rtes (PlannerInfo *root)
 
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)
 
Relids get_relids_in_jointree (Node *jtnode, bool include_outer_joins, bool include_inner_joins)
 
Relids get_relids_for_join (Query *query, int joinrelid)
 
void preprocess_targetlist (PlannerInfo *root)
 
Listextract_update_targetlist_colnos (List *tlist)
 
PlanRowMarkget_plan_rowmark (List *rowmarks, Index rtindex)
 
void get_agg_clause_costs (PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
 
void preprocess_aggrefs (PlannerInfo *root, Node *clause)
 
RelOptInfoplan_set_operations (PlannerInfo *root)
 

Function Documentation

◆ extract_update_targetlist_colnos()

List * extract_update_targetlist_colnos ( List tlist)
extern

Definition at line 350 of file preptlist.c.

351{
352 List *update_colnos = NIL;
354 ListCell *lc;
355
356 foreach(lc, tlist)
357 {
359
360 if (!tle->resjunk)
361 update_colnos = lappend_int(update_colnos, tle->resno);
362 tle->resno = nextresno++;
363 }
364 return update_colnos;
365}
int16 AttrNumber
Definition attnum.h:21
List * lappend_int(List *list, int datum)
Definition list.c:357
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
static int fb(int x)
Definition pg_list.h:54

References fb(), lappend_int(), lfirst, and NIL.

Referenced by make_modifytable(), and preprocess_targetlist().

◆ flatten_simple_union_all()

void flatten_simple_union_all ( PlannerInfo root)
extern

Definition at line 3114 of file prepjointree.c.

3115{
3116 Query *parse = root->parse;
3119 int leftmostRTI;
3121 int childRTI;
3124
3125 /* Shouldn't be called unless query has setops */
3126 topop = castNode(SetOperationStmt, parse->setOperations);
3127 Assert(topop);
3128
3129 /* Can't optimize away a recursive UNION */
3130 if (root->hasRecursion)
3131 return;
3132
3133 /*
3134 * Recursively check the tree of set operations. If not all UNION ALL
3135 * with identical column types, punt.
3136 */
3137 if (!is_simple_union_all_recurse((Node *) topop, parse, topop->colTypes))
3138 return;
3139
3140 /*
3141 * Locate the leftmost leaf query in the setops tree. The upper query's
3142 * Vars all refer to this RTE (see transformSetOperationStmt).
3143 */
3144 leftmostjtnode = topop->larg;
3148 leftmostRTI = ((RangeTblRef *) leftmostjtnode)->rtindex;
3150 Assert(leftmostRTE->rtekind == RTE_SUBQUERY);
3151
3152 /*
3153 * Make a copy of the leftmost RTE and add it to the rtable. This copy
3154 * will represent the leftmost leaf query in its capacity as a member of
3155 * the appendrel. The original will represent the appendrel as a whole.
3156 * (We must do things this way because the upper query's Vars have to be
3157 * seen as referring to the whole appendrel.)
3158 */
3160 parse->rtable = lappend(parse->rtable, childRTE);
3161 childRTI = list_length(parse->rtable);
3162
3163 /* Modify the setops tree to reference the child copy */
3164 ((RangeTblRef *) leftmostjtnode)->rtindex = childRTI;
3165
3166 /* Modify the formerly-leftmost RTE to mark it as an appendrel parent */
3167 leftmostRTE->inh = true;
3168
3169 /*
3170 * Form a RangeTblRef for the appendrel, and insert it into FROM. The top
3171 * Query of a setops tree should have had an empty FromClause initially.
3172 */
3174 rtr->rtindex = leftmostRTI;
3175 Assert(parse->jointree->fromlist == NIL);
3176 parse->jointree->fromlist = list_make1(rtr);
3177
3178 /*
3179 * Now pretend the query has no setops. We must do this before trying to
3180 * do subquery pullup, because of Assert in pull_up_simple_subquery.
3181 */
3182 parse->setOperations = NULL;
3183
3184 /*
3185 * Build AppendRelInfo information, and apply pull_up_subqueries to the
3186 * leaf queries of the UNION ALL. (We must do that now because they
3187 * weren't previously referenced by the jointree, and so were missed by
3188 * the main invocation of pull_up_subqueries.)
3189 */
3191}
#define Assert(condition)
Definition c.h:945
void parse(int)
Definition parse.c:49
List * lappend(List *list, void *datum)
Definition list.c:339
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define copyObject(obj)
Definition nodes.h:232
#define makeNode(_type_)
Definition nodes.h:161
#define castNode(_type_, nodeptr)
Definition nodes.h:182
@ RTE_SUBQUERY
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
static int list_length(const List *l)
Definition pg_list.h:152
#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)
tree ctl root
Definition radixtree.h:1857
Definition nodes.h:135

References Assert, castNode, copyObject, fb(), is_simple_union_all_recurse(), IsA, lappend(), list_length(), list_make1, makeNode, NIL, parse(), pull_up_union_leaf_queries(), root, rt_fetch, and RTE_SUBQUERY.

Referenced by subquery_planner().

◆ get_agg_clause_costs()

void get_agg_clause_costs ( PlannerInfo root,
AggSplit  aggsplit,
AggClauseCosts costs 
)
extern

Definition at line 559 of file prepagg.c.

560{
561 ListCell *lc;
562
563 foreach(lc, root->aggtransinfos)
564 {
566
567 /*
568 * Add the appropriate component function execution costs to
569 * appropriate totals.
570 */
571 if (DO_AGGSPLIT_COMBINE(aggsplit))
572 {
573 /* charge for combining previously aggregated states */
574 add_function_cost(root, transinfo->combinefn_oid, NULL,
575 &costs->transCost);
576 }
577 else
578 add_function_cost(root, transinfo->transfn_oid, NULL,
579 &costs->transCost);
580 if (DO_AGGSPLIT_DESERIALIZE(aggsplit) &&
581 OidIsValid(transinfo->deserialfn_oid))
582 add_function_cost(root, transinfo->deserialfn_oid, NULL,
583 &costs->transCost);
584 if (DO_AGGSPLIT_SERIALIZE(aggsplit) &&
585 OidIsValid(transinfo->serialfn_oid))
586 add_function_cost(root, transinfo->serialfn_oid, NULL,
587 &costs->finalCost);
588
589 /*
590 * These costs are incurred only by the initial aggregate node, so we
591 * mustn't include them again at upper levels.
592 */
593 if (!DO_AGGSPLIT_COMBINE(aggsplit))
594 {
595 /* add the input expressions' cost to per-input-row costs */
597
599 costs->transCost.startup += argcosts.startup;
600 costs->transCost.per_tuple += argcosts.per_tuple;
601
602 /*
603 * Add any filter's cost to per-input-row costs.
604 *
605 * XXX Ideally we should reduce input expression costs according
606 * to filter selectivity, but it's not clear it's worth the
607 * trouble.
608 */
609 if (transinfo->aggfilter)
610 {
612 root);
613 costs->transCost.startup += argcosts.startup;
614 costs->transCost.per_tuple += argcosts.per_tuple;
615 }
616 }
617
618 /*
619 * If the transition type is pass-by-value then it doesn't add
620 * anything to the required size of the hashtable. If it is
621 * pass-by-reference then we have to add the estimated size of the
622 * value itself, plus palloc overhead.
623 */
624 if (!transinfo->transtypeByVal)
625 {
627
628 /* Use average width if aggregate definition gave one */
629 if (transinfo->aggtransspace > 0)
630 avgwidth = transinfo->aggtransspace;
631 else if (transinfo->transfn_oid == F_ARRAY_APPEND)
632 {
633 /*
634 * If the transition function is array_append(), it'll use an
635 * expanded array as transvalue, which will occupy at least
636 * ALLOCSET_SMALL_INITSIZE and possibly more. Use that as the
637 * estimate for lack of a better idea.
638 */
640 }
641 else
642 {
643 avgwidth = get_typavgwidth(transinfo->aggtranstype, transinfo->aggtranstypmod);
644 }
645
647 costs->transitionSpace += avgwidth + 2 * sizeof(void *);
648 }
649 else if (transinfo->aggtranstype == INTERNALOID)
650 {
651 /*
652 * INTERNAL transition type is a special case: although INTERNAL
653 * is pass-by-value, it's almost certainly being used as a pointer
654 * to some large data structure. The aggregate definition can
655 * provide an estimate of the size. If it doesn't, then we assume
656 * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is
657 * being kept in a private memory context, as is done by
658 * array_agg() for instance.
659 */
660 if (transinfo->aggtransspace > 0)
661 costs->transitionSpace += transinfo->aggtransspace;
662 else
664 }
665 }
666
667 foreach(lc, root->agginfos)
668 {
670 Aggref *aggref = linitial_node(Aggref, agginfo->aggrefs);
671
672 /*
673 * Add the appropriate component function execution costs to
674 * appropriate totals.
675 */
676 if (!DO_AGGSPLIT_SKIPFINAL(aggsplit) &&
677 OidIsValid(agginfo->finalfn_oid))
678 add_function_cost(root, agginfo->finalfn_oid, NULL,
679 &costs->finalCost);
680
681 /*
682 * If there are direct arguments, treat their evaluation cost like the
683 * cost of the finalfn.
684 */
685 if (aggref->aggdirectargs)
686 {
688
690 root);
691 costs->finalCost.startup += argcosts.startup;
692 costs->finalCost.per_tuple += argcosts.per_tuple;
693 }
694 }
695}
#define MAXALIGN(LEN)
Definition c.h:898
int32_t int32
Definition c.h:614
#define OidIsValid(objectId)
Definition c.h:860
void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
Definition costsize.c:4925
int32 get_typavgwidth(Oid typid, int32 typmod)
Definition lsyscache.c:2800
#define ALLOCSET_SMALL_INITSIZE
Definition memutils.h:168
#define ALLOCSET_DEFAULT_INITSIZE
Definition memutils.h:158
#define DO_AGGSPLIT_SKIPFINAL(as)
Definition nodes.h:396
#define DO_AGGSPLIT_DESERIALIZE(as)
Definition nodes.h:398
#define DO_AGGSPLIT_COMBINE(as)
Definition nodes.h:395
#define DO_AGGSPLIT_SERIALIZE(as)
Definition nodes.h:397
#define lfirst_node(type, lc)
Definition pg_list.h:176
#define linitial_node(type, l)
Definition pg_list.h:181
void add_function_cost(PlannerInfo *root, Oid funcid, Node *node, QualCost *cost)
Definition plancat.c:2355
QualCost finalCost
Definition pathnodes.h:134
Size transitionSpace
Definition pathnodes.h:135
QualCost transCost
Definition pathnodes.h:133
List * aggdirectargs
Definition primnodes.h:485
Cost per_tuple
Definition pathnodes.h:121
Cost startup
Definition pathnodes.h:120

References add_function_cost(), Aggref::aggdirectargs, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_SMALL_INITSIZE, cost_qual_eval_node(), DO_AGGSPLIT_COMBINE, DO_AGGSPLIT_DESERIALIZE, DO_AGGSPLIT_SERIALIZE, DO_AGGSPLIT_SKIPFINAL, fb(), AggClauseCosts::finalCost, get_typavgwidth(), lfirst_node, linitial_node, MAXALIGN, OidIsValid, QualCost::per_tuple, root, QualCost::startup, AggClauseCosts::transCost, and AggClauseCosts::transitionSpace.

Referenced by create_grouping_paths(), create_partial_grouping_paths(), estimate_path_cost_size(), and generate_grouped_paths().

◆ get_plan_rowmark()

PlanRowMark * get_plan_rowmark ( List rowmarks,
Index  rtindex 
)
extern

Definition at line 528 of file preptlist.c.

529{
530 ListCell *l;
531
532 foreach(l, rowmarks)
533 {
534 PlanRowMark *rc = (PlanRowMark *) lfirst(l);
535
536 if (rc->rti == rtindex)
537 return rc;
538 }
539 return NULL;
540}

References fb(), lfirst, and PlanRowMark::rti.

Referenced by check_index_predicates(), deparseLockingClause(), and expand_inherited_rtentry().

◆ get_relids_for_join()

Relids get_relids_for_join ( Query query,
int  joinrelid 
)
extern

Definition at line 4574 of file prepjointree.c.

4575{
4576 Node *jtnode;
4577
4578 jtnode = find_jointree_node_for_rel((Node *) query->jointree,
4579 joinrelid);
4580 if (!jtnode)
4581 elog(ERROR, "could not find join node %d", joinrelid);
4582 return get_relids_in_jointree(jtnode, true, false);
4583}
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
static Node * find_jointree_node_for_rel(Node *jtnode, int relid)
Relids get_relids_in_jointree(Node *jtnode, bool include_outer_joins, bool include_inner_joins)
FromExpr * jointree
Definition parsenodes.h:182

References elog, ERROR, fb(), 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 
)
extern

Definition at line 4513 of file prepjointree.c.

4515{
4516 Relids result = NULL;
4517
4518 if (jtnode == NULL)
4519 return result;
4520 if (IsA(jtnode, RangeTblRef))
4521 {
4522 int varno = ((RangeTblRef *) jtnode)->rtindex;
4523
4524 result = bms_make_singleton(varno);
4525 }
4526 else if (IsA(jtnode, FromExpr))
4527 {
4528 FromExpr *f = (FromExpr *) jtnode;
4529 ListCell *l;
4530
4531 foreach(l, f->fromlist)
4532 {
4533 result = bms_join(result,
4537 }
4538 }
4539 else if (IsA(jtnode, JoinExpr))
4540 {
4541 JoinExpr *j = (JoinExpr *) jtnode;
4542
4543 result = get_relids_in_jointree(j->larg,
4546 result = bms_join(result,
4550 if (j->rtindex)
4551 {
4552 if (j->jointype == JOIN_INNER)
4553 {
4555 result = bms_add_member(result, j->rtindex);
4556 }
4557 else
4558 {
4560 result = bms_add_member(result, j->rtindex);
4561 }
4562 }
4563 }
4564 else
4565 elog(ERROR, "unrecognized node type: %d",
4566 (int) nodeTag(jtnode));
4567 return result;
4568}
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:216
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition bitmapset.c:1214
int j
Definition isn.c:78
#define nodeTag(nodeptr)
Definition nodes.h:139
@ JOIN_INNER
Definition nodes.h:303
List * fromlist
Definition primnodes.h:2383

References bms_add_member(), bms_join(), bms_make_singleton(), elog, ERROR, fb(), 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().

◆ plan_set_operations()

RelOptInfo * plan_set_operations ( PlannerInfo root)
extern

Definition at line 98 of file prepunion.c.

99{
100 Query *parse = root->parse;
102 Node *node;
107
108 Assert(topop);
109
110 /* check for unsupported stuff */
111 Assert(parse->jointree->fromlist == NIL);
112 Assert(parse->jointree->quals == NULL);
113 Assert(parse->groupClause == NIL);
114 Assert(parse->havingQual == NULL);
115 Assert(parse->windowClause == NIL);
116 Assert(parse->distinctClause == NIL);
117
118 /*
119 * In the outer query level, equivalence classes are limited to classes
120 * which define that the top-level target entry is equivalent to the
121 * corresponding child target entry. There won't be any equivalence class
122 * merging. Mark that merging is complete to allow us to make pathkeys.
123 */
124 Assert(root->eq_classes == NIL);
125 root->ec_merging_done = true;
126
127 /*
128 * We'll need to build RelOptInfos for each of the leaf subqueries, which
129 * are RTE_SUBQUERY rangetable entries in this Query. Prepare the index
130 * arrays for those, and for AppendRelInfos in case they're needed.
131 */
133
134 /*
135 * Find the leftmost component Query. We need to use its column names for
136 * all generated tlists (else SELECT INTO won't work right).
137 */
138 node = topop->larg;
139 while (node && IsA(node, SetOperationStmt))
140 node = ((SetOperationStmt *) node)->larg;
141 Assert(node && IsA(node, RangeTblRef));
142 leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex];
143 leftmostQuery = leftmostRTE->subquery;
145
146 /*
147 * If the topmost node is a recursive union, it needs special processing.
148 */
149 if (root->hasRecursion)
150 {
152 leftmostQuery->targetList,
153 &top_tlist);
154 }
155 else
156 {
157 bool trivial_tlist;
158
159 /*
160 * Recurse on setOperations tree to generate paths for set ops. The
161 * final output paths should have just the column types shown as the
162 * output from the top-level node.
163 */
165 NULL, /* no parent */
166 topop->colTypes, topop->colCollations,
167 leftmostQuery->targetList,
168 &top_tlist,
170 }
171
172 /* Must return the built tlist into root->processed_tlist. */
173 root->processed_tlist = top_tlist;
174
175 return setop_rel;
176}
static RelOptInfo * recurse_set_operations(Node *setOp, PlannerInfo *root, SetOperationStmt *parentOp, List *colTypes, List *colCollations, List *refnames_tlist, List **pTargetList, bool *istrivial_tlist)
Definition prepunion.c:214
static RelOptInfo * generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root, List *refnames_tlist, List **pTargetList)
Definition prepunion.c:365
void setup_simple_rel_arrays(PlannerInfo *root)
Definition relnode.c:114

References Assert, castNode, fb(), generate_recursion_path(), IsA, NIL, parse(), recurse_set_operations(), root, and setup_simple_rel_arrays().

Referenced by grouping_planner().

◆ preprocess_aggrefs()

void preprocess_aggrefs ( PlannerInfo root,
Node clause 
)
extern

Definition at line 110 of file prepagg.c.

111{
113}
static bool preprocess_aggrefs_walker(Node *node, PlannerInfo *root)
Definition prepagg.c:344

References fb(), preprocess_aggrefs_walker(), and root.

Referenced by grouping_planner().

◆ preprocess_function_rtes()

void preprocess_function_rtes ( PlannerInfo root)
extern

Definition at line 1154 of file prepjointree.c.

1155{
1156 ListCell *rt;
1157
1158 foreach(rt, root->parse->rtable)
1159 {
1161
1162 if (rte->rtekind == RTE_FUNCTION)
1163 {
1165
1166 /* Apply const-simplification */
1167 rte->functions = (List *)
1168 eval_const_expressions(root, (Node *) rte->functions);
1169
1170 /* Check safety of expansion, and expand if possible */
1172 if (funcquery)
1173 {
1174 /* Successful expansion, convert the RTE to a subquery */
1175 rte->rtekind = RTE_SUBQUERY;
1176 rte->subquery = funcquery;
1177 rte->security_barrier = false;
1178
1179 /*
1180 * Clear fields that should not be set in a subquery RTE.
1181 * However, we leave rte->functions filled in for the moment,
1182 * in case makeWholeRowVar needs to consult it. We'll clear
1183 * it in setrefs.c (see add_rte_to_flat_rtable) so that this
1184 * abuse of the data structure doesn't escape the planner.
1185 */
1186 rte->funcordinality = false;
1187 }
1188 }
1189 }
1190}
Query * inline_function_in_from(PlannerInfo *root, RangeTblEntry *rte)
Definition clauses.c:5780
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition clauses.c:2498
@ RTE_FUNCTION

References eval_const_expressions(), fb(), inline_function_in_from(), lfirst, root, RTE_FUNCTION, and RTE_SUBQUERY.

Referenced by pull_up_simple_subquery(), and subquery_planner().

◆ preprocess_relation_rtes()

Query * preprocess_relation_rtes ( PlannerInfo root)
extern

Definition at line 421 of file prepjointree.c.

422{
423 Query *parse = root->parse;
424 int rtable_size;
425 int rt_index;
426
427 rtable_size = list_length(parse->rtable);
428
429 for (rt_index = 0; rt_index < rtable_size; rt_index++)
430 {
431 RangeTblEntry *rte = rt_fetch(rt_index + 1, parse->rtable);
432 Relation relation;
433
434 /* We only care about relation RTEs. */
435 if (rte->rtekind != RTE_RELATION)
436 continue;
437
438 /*
439 * We need not lock the relation since it was already locked by the
440 * rewriter.
441 */
442 relation = table_open(rte->relid, NoLock);
443
444 /*
445 * Check to see if the relation actually has any children; if not,
446 * clear the inh flag so we can treat it as a plain base relation.
447 *
448 * Note: this could give a false-positive result, if the rel once had
449 * children but no longer does. We used to be able to clear rte->inh
450 * later on when we discovered that, but no more; we have to handle
451 * such cases as full-fledged inheritance.
452 */
453 if (rte->inh)
454 rte->inh = relation->rd_rel->relhassubclass;
455
456 /*
457 * Check to see if the relation has any column not-null constraints;
458 * if so, retrieve the constraint information and store it in a
459 * relation OID based hash table.
460 */
462
463 /*
464 * Check to see if the relation has any virtual generated columns; if
465 * so, replace all Var nodes in the query that reference these columns
466 * with the generation expressions.
467 */
469 rte, rt_index + 1,
470 relation);
471
472 table_close(relation, NoLock);
473 }
474
475 return parse;
476}
#define NoLock
Definition lockdefs.h:34
@ RTE_RELATION
void get_relation_notnullatts(PlannerInfo *root, Relation relation)
Definition plancat.c:690
static Query * expand_virtual_generated_columns(PlannerInfo *root, Query *parse, RangeTblEntry *rte, int rt_index, Relation relation)
Form_pg_class rd_rel
Definition rel.h:111
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40

References expand_virtual_generated_columns(), fb(), get_relation_notnullatts(), list_length(), NoLock, parse(), RelationData::rd_rel, root, rt_fetch, RTE_RELATION, table_close(), and table_open().

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

◆ preprocess_targetlist()

void preprocess_targetlist ( PlannerInfo root)
extern

Definition at line 66 of file preptlist.c.

67{
68 Query *parse = root->parse;
69 int result_relation = parse->resultRelation;
70 List *range_table = parse->rtable;
71 CmdType command_type = parse->commandType;
72 RangeTblEntry *target_rte = NULL;
74 List *tlist;
75 ListCell *lc;
76
77 /*
78 * If there is a result relation, open it so we can look for missing
79 * columns and so on. We assume that previous code already acquired at
80 * least AccessShareLock on the relation, so we need no lock here.
81 */
82 if (result_relation)
83 {
84 target_rte = rt_fetch(result_relation, range_table);
85
86 /*
87 * Sanity check: it'd better be a real relation not, say, a subquery.
88 * Else parser or rewriter messed up.
89 */
90 if (target_rte->rtekind != RTE_RELATION)
91 elog(ERROR, "result relation must be a regular relation");
92
93 target_relation = table_open(target_rte->relid, NoLock);
94 }
95 else
97
98 /*
99 * In an INSERT, the executor expects the targetlist to match the exact
100 * order of the target table's attributes, including entries for
101 * attributes not mentioned in the source query.
102 *
103 * In an UPDATE, we don't rearrange the tlist order, but we need to make a
104 * separate list of the target attribute numbers, in tlist order, and then
105 * renumber the processed_tlist entries to be consecutive.
106 */
107 tlist = parse->targetList;
110 else if (command_type == CMD_UPDATE)
111 root->update_colnos = extract_update_targetlist_colnos(tlist);
112
113 /*
114 * For non-inherited UPDATE/DELETE/MERGE, register any junk column(s)
115 * needed to allow the executor to identify the rows to be updated or
116 * deleted. In the inheritance case, we do nothing now, leaving this to
117 * be dealt with when expand_inherited_rtentry() makes the leaf target
118 * relations. (But there might not be any leaf target relations, in which
119 * case we must do this in distribute_row_identity_vars().)
120 */
123 !target_rte->inh)
124 {
125 /* row-identity logic expects to add stuff to processed_tlist */
126 root->processed_tlist = tlist;
127 add_row_identity_columns(root, result_relation,
128 target_rte, target_relation);
129 tlist = root->processed_tlist;
130 }
131
132 /*
133 * For MERGE we also need to handle the target list for each INSERT and
134 * UPDATE action separately. In addition, we examine the qual of each
135 * action and add any Vars there (other than those of the target rel) to
136 * the subplan targetlist.
137 */
138 if (command_type == CMD_MERGE)
139 {
140 ListCell *l;
141 List *vars;
142
143 /*
144 * For MERGE, handle targetlist of each MergeAction separately. Give
145 * the same treatment to MergeAction->targetList as we would have
146 * given to a regular INSERT. For UPDATE, collect the column numbers
147 * being modified.
148 */
149 foreach(l, parse->mergeActionList)
150 {
152 ListCell *l2;
153
154 if (action->commandType == CMD_INSERT)
156 action->targetList,
158 else if (action->commandType == CMD_UPDATE)
159 action->updateColnos =
161
162 /*
163 * Add resjunk entries for any Vars and PlaceHolderVars used in
164 * each action's targetlist and WHEN condition that belong to
165 * relations other than the target. We don't expect to see any
166 * aggregates or window functions here.
167 */
169 list_concat_copy((List *) action->qual,
170 action->targetList),
172 foreach(l2, vars)
173 {
174 Var *var = (Var *) lfirst(l2);
176
177 if (IsA(var, Var) && var->varno == result_relation)
178 continue; /* don't need it */
179
180 if (tlist_member((Expr *) var, tlist))
181 continue; /* already got it */
182
183 tle = makeTargetEntry((Expr *) var,
184 list_length(tlist) + 1,
185 NULL, true);
186 tlist = lappend(tlist, tle);
187 }
189 }
190
191 /*
192 * Add resjunk entries for any Vars and PlaceHolderVars used in the
193 * join condition that belong to relations other than the target. We
194 * don't expect to see any aggregates or window functions here.
195 */
196 vars = pull_var_clause(parse->mergeJoinCondition,
198 foreach(l, vars)
199 {
200 Var *var = (Var *) lfirst(l);
202
203 if (IsA(var, Var) && var->varno == result_relation)
204 continue; /* don't need it */
205
206 if (tlist_member((Expr *) var, tlist))
207 continue; /* already got it */
208
209 tle = makeTargetEntry((Expr *) var,
210 list_length(tlist) + 1,
211 NULL, true);
212 tlist = lappend(tlist, tle);
213 }
214 }
215
216 /*
217 * Add necessary junk columns for rowmarked rels. These values are needed
218 * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
219 * rechecking. See comments for PlanRowMark in plannodes.h. If you
220 * change this stanza, see also expand_inherited_rtentry(), which has to
221 * be able to add on junk columns equivalent to these.
222 *
223 * (Someday it might be useful to fold these resjunk columns into the
224 * row-identity-column management used for UPDATE/DELETE. Today is not
225 * that day, however. One notable issue is that it seems important that
226 * the whole-row Vars made here use the real table rowtype, not RECORD, so
227 * that conversion to/from child relations' rowtypes will happen. Also,
228 * since these entries don't potentially bloat with more and more child
229 * relations, there's not really much need for column sharing.)
230 */
231 foreach(lc, root->rowMarks)
232 {
233 PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
234 Var *var;
235 char resname[32];
237
238 /* child rels use the same junk attrs as their parents */
239 if (rc->rti != rc->prti)
240 continue;
241
242 if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
243 {
244 /* Need to fetch TID */
245 var = makeVar(rc->rti,
247 TIDOID,
248 -1,
250 0);
251 snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
252 tle = makeTargetEntry((Expr *) var,
253 list_length(tlist) + 1,
255 true);
256 tlist = lappend(tlist, tle);
257 }
258 if (rc->allMarkTypes & (1 << ROW_MARK_COPY))
259 {
260 /* Need the whole row as a junk var */
261 var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
262 rc->rti,
263 0,
264 false);
265 snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
266 tle = makeTargetEntry((Expr *) var,
267 list_length(tlist) + 1,
269 true);
270 tlist = lappend(tlist, tle);
271 }
272
273 /* If parent of inheritance tree, always fetch the tableoid too. */
274 if (rc->isParent)
275 {
276 var = makeVar(rc->rti,
278 OIDOID,
279 -1,
281 0);
282 snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
283 tle = makeTargetEntry((Expr *) var,
284 list_length(tlist) + 1,
286 true);
287 tlist = lappend(tlist, tle);
288 }
289 }
290
291 /*
292 * If the query has a RETURNING list, add resjunk entries for any Vars
293 * used in RETURNING that belong to other relations. We need to do this
294 * to make these Vars available for the RETURNING calculation. Vars that
295 * belong to the result rel don't need to be added, because they will be
296 * made to refer to the actual heap tuple.
297 */
298 if (parse->returningList && list_length(parse->rtable) > 1)
299 {
300 List *vars;
301 ListCell *l;
302
303 vars = pull_var_clause((Node *) parse->returningList,
307 foreach(l, vars)
308 {
309 Var *var = (Var *) lfirst(l);
311
312 if (IsA(var, Var) &&
313 var->varno == result_relation)
314 continue; /* don't need it */
315
316 if (tlist_member((Expr *) var, tlist))
317 continue; /* already got it */
318
319 tle = makeTargetEntry((Expr *) var,
320 list_length(tlist) + 1,
321 NULL,
322 true);
323
324 tlist = lappend(tlist, tle);
325 }
327 }
328
329 root->processed_tlist = tlist;
330
331 if (target_relation)
333}
void add_row_identity_columns(PlannerInfo *root, Index rtindex, RangeTblEntry *target_rte, Relation target_relation)
Definition appendinfo.c:959
List * list_concat_copy(const List *list1, const List *list2)
Definition list.c:598
void list_free(List *list)
Definition list.c:1546
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition makefuncs.c:66
Var * makeWholeRowVar(RangeTblEntry *rte, int varno, Index varlevelsup, bool allowScalar)
Definition makefuncs.c:137
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition makefuncs.c:289
char * pstrdup(const char *in)
Definition mcxt.c:1781
CmdType
Definition nodes.h:273
@ CMD_MERGE
Definition nodes.h:279
@ CMD_INSERT
Definition nodes.h:277
@ CMD_DELETE
Definition nodes.h:278
@ CMD_UPDATE
Definition nodes.h:276
@ CMD_SELECT
Definition nodes.h:275
#define PVC_RECURSE_AGGREGATES
Definition optimizer.h:198
#define PVC_RECURSE_WINDOWFUNCS
Definition optimizer.h:200
#define PVC_INCLUDE_PLACEHOLDERS
Definition optimizer.h:201
@ ROW_MARK_COPY
Definition plannodes.h:1558
#define snprintf
Definition port.h:260
#define InvalidOid
List * extract_update_targetlist_colnos(List *tlist)
Definition preptlist.c:350
static List * expand_insert_targetlist(PlannerInfo *root, List *tlist, Relation rel)
Definition preptlist.c:384
Index rowmarkId
Definition plannodes.h:1611
RTEKind rtekind
int varno
Definition primnodes.h:270
#define TableOidAttributeNumber
Definition sysattr.h:26
#define SelfItemPointerAttributeNumber
Definition sysattr.h:21
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition tlist.c:88
List * pull_var_clause(Node *node, int flags)
Definition var.c:653

References add_row_identity_columns(), PlanRowMark::allMarkTypes, Assert, CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_SELECT, CMD_UPDATE, elog, ERROR, expand_insert_targetlist(), extract_update_targetlist_colnos(), fb(), RangeTblEntry::inh, InvalidOid, IsA, PlanRowMark::isParent, lappend(), lfirst, list_concat_copy(), list_free(), list_length(), makeTargetEntry(), makeVar(), makeWholeRowVar(), NoLock, parse(), PlanRowMark::prti, pstrdup(), pull_var_clause(), PVC_INCLUDE_PLACEHOLDERS, PVC_RECURSE_AGGREGATES, PVC_RECURSE_WINDOWFUNCS, root, ROW_MARK_COPY, PlanRowMark::rowmarkId, rt_fetch, RTE_RELATION, RangeTblEntry::rtekind, PlanRowMark::rti, SelfItemPointerAttributeNumber, snprintf, table_close(), table_open(), TableOidAttributeNumber, tlist_member(), and Var::varno.

Referenced by grouping_planner().

◆ pull_up_sublinks()

void pull_up_sublinks ( PlannerInfo root)
extern

Definition at line 651 of file prepjointree.c.

652{
653 Node *jtnode;
654 Relids relids;
655
656 /* Begin recursion through the jointree */
658 (Node *) root->parse->jointree,
659 &relids);
660
661 /*
662 * root->parse->jointree must always be a FromExpr, so insert a dummy one
663 * if we got a bare RangeTblRef or JoinExpr out of the recursion.
664 */
665 if (IsA(jtnode, FromExpr))
666 root->parse->jointree = (FromExpr *) jtnode;
667 else
668 root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL);
669}
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition makefuncs.c:336
static Node * pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode, Relids *relids)

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

Referenced by pull_up_simple_subquery(), and subquery_planner().

◆ pull_up_subqueries()

void pull_up_subqueries ( PlannerInfo root)
extern

Definition at line 1201 of file prepjointree.c.

1202{
1203 /* Top level of jointree must always be a FromExpr */
1204 Assert(IsA(root->parse->jointree, FromExpr));
1205 /* Recursion starts with no containing join nor appendrel */
1206 root->parse->jointree = (FromExpr *)
1207 pull_up_subqueries_recurse(root, (Node *) root->parse->jointree,
1208 NULL, NULL);
1209 /* We should still have a FromExpr */
1210 Assert(IsA(root->parse->jointree, FromExpr));
1211}
static Node * pull_up_subqueries_recurse(PlannerInfo *root, Node *jtnode, JoinExpr *lowest_outer_join, AppendRelInfo *containing_appendrel)

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

Referenced by pull_up_simple_subquery(), and subquery_planner().

◆ reduce_outer_joins()

void reduce_outer_joins ( PlannerInfo root)
extern

Definition at line 3236 of file prepjointree.c.

3237{
3240 ListCell *lc;
3241
3242 /*
3243 * To avoid doing strictness checks on more quals than necessary, we want
3244 * to stop descending the jointree as soon as there are no outer joins
3245 * below our current point. This consideration forces a two-pass process.
3246 * The first pass gathers information about which base rels appear below
3247 * each side of each join clause, about whether there are outer join(s)
3248 * below each side of each join clause, and about which base rels are from
3249 * the nullable side of those outer join(s). The second pass examines
3250 * qual clauses and changes join types as it descends the tree.
3251 */
3252 state1 = reduce_outer_joins_pass1((Node *) root->parse->jointree);
3253
3254 /* planner.c shouldn't have called me if no outer joins */
3255 if (state1 == NULL || !state1->contains_outer)
3256 elog(ERROR, "so where are the outer joins?");
3257
3258 state2.inner_reduced = NULL;
3259 state2.partial_reduced = NIL;
3260
3261 reduce_outer_joins_pass2((Node *) root->parse->jointree,
3262 state1, &state2,
3263 root, NULL, NIL);
3264
3265 /*
3266 * If we successfully reduced the strength of any outer joins, we must
3267 * remove references to those joins as nulling rels. This is handled as
3268 * an additional pass, for simplicity and because we can handle all
3269 * fully-reduced joins in a single pass over the parse tree.
3270 */
3271 if (!bms_is_empty(state2.inner_reduced))
3272 {
3273 root->parse = (Query *)
3274 remove_nulling_relids((Node *) root->parse,
3275 state2.inner_reduced,
3276 NULL);
3277 /* There could be references in the append_rel_list, too */
3278 root->append_rel_list = (List *)
3279 remove_nulling_relids((Node *) root->append_rel_list,
3280 state2.inner_reduced,
3281 NULL);
3282 }
3283
3284 /*
3285 * Partially-reduced full joins have to be done one at a time, since
3286 * they'll each need a different setting of except_relids.
3287 */
3288 foreach(lc, state2.partial_reduced)
3289 {
3292
3293 root->parse = (Query *)
3294 remove_nulling_relids((Node *) root->parse,
3296 statep->unreduced_side);
3297 root->append_rel_list = (List *)
3298 remove_nulling_relids((Node *) root->append_rel_list,
3300 statep->unreduced_side);
3301 }
3302}
#define bms_is_empty(a)
Definition bitmapset.h:118
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(), elog, ERROR, fb(), lfirst, NIL, reduce_outer_joins_pass1(), reduce_outer_joins_pass2(), remove_nulling_relids(), and root.

Referenced by subquery_planner().

◆ remove_useless_result_rtes()

void remove_useless_result_rtes ( PlannerInfo root)
extern

Definition at line 3867 of file prepjointree.c.

3868{
3870 ListCell *cell;
3871
3872 /* Top level of jointree must always be a FromExpr */
3873 Assert(IsA(root->parse->jointree, FromExpr));
3874 /* Recurse ... */
3875 root->parse->jointree = (FromExpr *)
3877 (Node *) root->parse->jointree,
3878 NULL,
3880 /* We should still have a FromExpr */
3881 Assert(IsA(root->parse->jointree, FromExpr));
3882
3883 /*
3884 * If we removed any outer-join nodes from the jointree, run around and
3885 * remove references to those joins as nulling rels. (There could be such
3886 * references in PHVs that we pulled up out of the original subquery that
3887 * the RESULT rel replaced. This is kosher on the grounds that we now
3888 * know that such an outer join wouldn't really have nulled anything.) We
3889 * don't do this during the main recursion, for simplicity and because we
3890 * can handle all such joins in a single pass over the parse tree.
3891 */
3893 {
3894 root->parse = (Query *)
3895 remove_nulling_relids((Node *) root->parse,
3897 NULL);
3898 /* There could be references in the append_rel_list, too */
3899 root->append_rel_list = (List *)
3900 remove_nulling_relids((Node *) root->append_rel_list,
3902 NULL);
3903 }
3904
3905 /*
3906 * Remove any PlanRowMark referencing an RTE_RESULT RTE. We obviously
3907 * must do that for any RTE_RESULT that we just removed. But one for a
3908 * RTE that we did not remove can be dropped anyway: since the RTE has
3909 * only one possible output row, there is no need for EPQ to mark and
3910 * restore that row.
3911 *
3912 * It's necessary, not optional, to remove the PlanRowMark for a surviving
3913 * RTE_RESULT RTE; otherwise we'll generate a whole-row Var for the
3914 * RTE_RESULT, which the executor has no support for.
3915 */
3916 foreach(cell, root->rowMarks)
3917 {
3918 PlanRowMark *rc = (PlanRowMark *) lfirst(cell);
3919
3920 if (rt_fetch(rc->rti, root->parse->rtable)->rtekind == RTE_RESULT)
3921 root->rowMarks = foreach_delete_current(root->rowMarks, cell);
3922 }
3923}
@ RTE_RESULT
#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, fb(), foreach_delete_current, IsA, lfirst, remove_nulling_relids(), remove_useless_results_recurse(), root, rt_fetch, RTE_RESULT, and PlanRowMark::rti.

Referenced by subquery_planner().

◆ replace_empty_jointree()

void replace_empty_jointree ( Query parse)
extern

Definition at line 593 of file prepjointree.c.

594{
596 Index rti;
598
599 /* Nothing to do if jointree is already nonempty */
600 if (parse->jointree->fromlist != NIL)
601 return;
602
603 /* We mustn't change it in the top level of a setop tree, either */
604 if (parse->setOperations)
605 return;
606
607 /* Create suitable RTE */
609 rte->rtekind = RTE_RESULT;
610 rte->eref = makeAlias("*RESULT*", NIL);
611
612 /* Add it to rangetable */
613 parse->rtable = lappend(parse->rtable, rte);
614 rti = list_length(parse->rtable);
615
616 /* And jam a reference into the jointree */
618 rtr->rtindex = rti;
619 parse->jointree->fromlist = list_make1(rtr);
620}
unsigned int Index
Definition c.h:700
Alias * makeAlias(const char *aliasname, List *colnames)
Definition makefuncs.c:438

References fb(), lappend(), list_length(), list_make1, makeAlias(), makeNode, NIL, parse(), and RTE_RESULT.

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

◆ transform_MERGE_to_join()

void transform_MERGE_to_join ( Query parse)
extern

Definition at line 191 of file prepjointree.c.

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

References add_nulling_relids(), Assert, bms_make_singleton(), CMD_MERGE, CMD_NOTHING, elog, ERROR, fb(), foreach_node, IS_NOT_NULL, IsA, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, lappend(), linitial, list_length(), list_make1, Var::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, NUM_MERGE_MATCH_KINDS, parse(), rt_fetch, RTE_JOIN, and source.

Referenced by subquery_planner().