#include "postgres.h"
#include "catalog/pg_type.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/placeholder.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/var.h"
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
Go to the source code of this file.
Functions | |
static void | extract_lateral_references (PlannerInfo *root, RelOptInfo *brel, Index rtindex) |
static void | add_lateral_info (PlannerInfo *root, Index rhs, Relids lhs) |
static List * | deconstruct_recurse (PlannerInfo *root, Node *jtnode, bool below_outer_join, Relids *qualscope, Relids *inner_join_rels) |
static SpecialJoinInfo * | make_outerjoininfo (PlannerInfo *root, Relids left_rels, Relids right_rels, Relids inner_join_rels, JoinType jointype, List *clause) |
static void | distribute_qual_to_rels (PlannerInfo *root, Node *clause, bool is_deduced, bool below_outer_join, JoinType jointype, Relids qualscope, Relids ojscope, Relids outerjoin_nonnullable, Relids deduced_nullable_relids) |
static bool | check_outerjoin_delay (PlannerInfo *root, Relids *relids_p, Relids *nullable_relids_p, bool is_pushed_down) |
static bool | check_equivalence_delay (PlannerInfo *root, RestrictInfo *restrictinfo) |
static bool | check_redundant_nullability_qual (PlannerInfo *root, Node *clause) |
static void | check_mergejoinable (RestrictInfo *restrictinfo) |
static void | check_hashjoinable (RestrictInfo *restrictinfo) |
void | add_base_rels_to_query (PlannerInfo *root, Node *jtnode) |
void | build_base_rel_tlists (PlannerInfo *root, List *final_tlist) |
void | add_vars_to_targetlist (PlannerInfo *root, List *vars, Relids where_needed, bool create_new_ph) |
void | find_lateral_references (PlannerInfo *root) |
void | create_lateral_join_info (PlannerInfo *root) |
List * | deconstruct_jointree (PlannerInfo *root) |
void | distribute_restrictinfo_to_rels (PlannerInfo *root, RestrictInfo *restrictinfo) |
void | process_implied_equality (PlannerInfo *root, Oid opno, Oid collation, Expr *item1, Expr *item2, Relids qualscope, Relids nullable_relids, bool below_outer_join, bool both_const) |
RestrictInfo * | build_implied_join_equality (Oid opno, Oid collation, Expr *item1, Expr *item2, Relids qualscope, Relids nullable_relids) |
Variables | |
int | from_collapse_limit |
int | join_collapse_limit |
void add_base_rels_to_query | ( | PlannerInfo * | root, | |
Node * | jtnode | |||
) |
Definition at line 88 of file initsplan.c.
References add_base_rels_to_query(), build_simple_rel(), elog, ERROR, FromExpr::fromlist, IsA, JoinExpr::larg, lfirst, nodeTag, NULL, JoinExpr::rarg, and RELOPT_BASEREL.
Referenced by add_base_rels_to_query(), and query_planner().
{ if (jtnode == NULL) return; if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; (void) build_simple_rel(root, varno, RELOPT_BASEREL); } else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; ListCell *l; foreach(l, f->fromlist) add_base_rels_to_query(root, lfirst(l)); } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; add_base_rels_to_query(root, j->larg); add_base_rels_to_query(root, j->rarg); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); }
static void add_lateral_info | ( | PlannerInfo * | root, | |
Index | rhs, | |||
Relids | lhs | |||
) | [static] |
Definition at line 473 of file initsplan.c.
References Assert, bms_free(), bms_is_member(), bms_is_subset(), lappend(), PlannerInfo::lateral_info_list, LateralJoinInfo::lateral_lhs, LateralJoinInfo::lateral_rhs, lfirst, and makeNode.
Referenced by create_lateral_join_info().
{ LateralJoinInfo *ljinfo; ListCell *l; Assert(!bms_is_member(rhs, lhs)); /* * If an existing list member has the same RHS and an LHS that is a subset * of the new one, it's redundant, but we don't trouble to get rid of it. * The only case that is really worth worrying about is identical entries, * and we handle that well enough with this simple logic. */ foreach(l, root->lateral_info_list) { ljinfo = (LateralJoinInfo *) lfirst(l); if (rhs == ljinfo->lateral_rhs && bms_is_subset(lhs, ljinfo->lateral_lhs)) { bms_free(lhs); return; } } /* Not there, so make a new entry */ ljinfo = makeNode(LateralJoinInfo); ljinfo->lateral_rhs = rhs; ljinfo->lateral_lhs = lhs; root->lateral_info_list = lappend(root->lateral_info_list, ljinfo); }
void add_vars_to_targetlist | ( | PlannerInfo * | root, | |
List * | vars, | |||
Relids | where_needed, | |||
bool | create_new_ph | |||
) |
Definition at line 163 of file initsplan.c.
References Assert, RelOptInfo::attr_needed, bms_add_members(), bms_is_empty(), bms_is_subset(), copyObject(), elog, ERROR, find_base_rel(), find_placeholder_info(), IsA, lappend(), lfirst, mark_placeholder_maybe_needed(), RelOptInfo::min_attr, nodeTag, NULL, PlaceHolderInfo::ph_may_need, PlaceHolderInfo::ph_needed, RelOptInfo::reltargetlist, RangeQueryClause::var, Var::varattno, and Var::varno.
Referenced by build_base_rel_tlists(), distribute_qual_to_rels(), extract_lateral_references(), fix_placeholder_input_needed_levels(), and generate_base_implied_equalities_no_const().
{ ListCell *temp; Assert(!bms_is_empty(where_needed)); foreach(temp, vars) { Node *node = (Node *) lfirst(temp); if (IsA(node, Var)) { Var *var = (Var *) node; RelOptInfo *rel = find_base_rel(root, var->varno); int attno = var->varattno; Assert(attno >= rel->min_attr && attno <= rel->max_attr); attno -= rel->min_attr; if (rel->attr_needed[attno] == NULL) { /* Variable not yet requested, so add to reltargetlist */ /* XXX is copyObject necessary here? */ rel->reltargetlist = lappend(rel->reltargetlist, copyObject(var)); } rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno], where_needed); } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, create_new_ph); /* Always adjust ph_needed */ phinfo->ph_needed = bms_add_members(phinfo->ph_needed, where_needed); /* * If we are creating PlaceHolderInfos, mark them with the correct * maybe-needed locations. Otherwise, it's too late to change * that, so we'd better not have set ph_needed to more than * ph_may_need. */ if (create_new_ph) mark_placeholder_maybe_needed(root, phinfo, where_needed); else Assert(bms_is_subset(phinfo->ph_needed, phinfo->ph_may_need)); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); } }
void build_base_rel_tlists | ( | PlannerInfo * | root, | |
List * | final_tlist | |||
) |
Definition at line 134 of file initsplan.c.
References add_vars_to_targetlist(), bms_make_singleton(), list_free(), NIL, pull_var_clause(), PVC_INCLUDE_PLACEHOLDERS, and PVC_RECURSE_AGGREGATES.
Referenced by query_planner().
{ List *tlist_vars = pull_var_clause((Node *) final_tlist, PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); if (tlist_vars != NIL) { add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0), true); list_free(tlist_vars); } }
RestrictInfo* build_implied_join_equality | ( | Oid | opno, | |
Oid | collation, | |||
Expr * | item1, | |||
Expr * | item2, | |||
Relids | qualscope, | |||
Relids | nullable_relids | |||
) |
Definition at line 1803 of file initsplan.c.
References BOOLOID, check_hashjoinable(), check_mergejoinable(), copyObject(), InvalidOid, make_opclause(), make_restrictinfo(), and NULL.
Referenced by create_join_clause(), reconsider_full_join_clause(), and reconsider_outer_join_clause().
{ RestrictInfo *restrictinfo; Expr *clause; /* * Build the new clause. Copy to ensure it shares no substructure with * original (this is necessary in case there are subselects in there...) */ clause = make_opclause(opno, BOOLOID, /* opresulttype */ false, /* opretset */ (Expr *) copyObject(item1), (Expr *) copyObject(item2), InvalidOid, collation); /* * Build the RestrictInfo node itself. */ restrictinfo = make_restrictinfo(clause, true, /* is_pushed_down */ false, /* outerjoin_delayed */ false, /* pseudoconstant */ qualscope, /* required_relids */ NULL, /* outer_relids */ nullable_relids); /* nullable_relids */ /* Set mergejoinability/hashjoinability flags */ check_mergejoinable(restrictinfo); check_hashjoinable(restrictinfo); return restrictinfo; }
static bool check_equivalence_delay | ( | PlannerInfo * | root, | |
RestrictInfo * | restrictinfo | |||
) | [static] |
Definition at line 1589 of file initsplan.c.
References bms_copy(), check_outerjoin_delay(), PlannerInfo::join_info_list, RestrictInfo::left_relids, NIL, and RestrictInfo::right_relids.
Referenced by distribute_qual_to_rels().
{ Relids relids; Relids nullable_relids; /* fast path if no special joins */ if (root->join_info_list == NIL) return true; /* must copy restrictinfo's relids to avoid changing it */ relids = bms_copy(restrictinfo->left_relids); /* check left side does not need delay */ if (check_outerjoin_delay(root, &relids, &nullable_relids, true)) return false; /* and similarly for the right side */ relids = bms_copy(restrictinfo->right_relids); if (check_outerjoin_delay(root, &relids, &nullable_relids, true)) return false; return true; }
static void check_hashjoinable | ( | RestrictInfo * | restrictinfo | ) | [static] |
Definition at line 1897 of file initsplan.c.
References RestrictInfo::clause, contain_volatile_functions(), exprType(), RestrictInfo::hashjoinoperator, is_opclause, linitial, list_length(), op_hashjoinable(), and RestrictInfo::pseudoconstant.
Referenced by build_implied_join_equality(), and distribute_restrictinfo_to_rels().
{ Expr *clause = restrictinfo->clause; Oid opno; Node *leftarg; if (restrictinfo->pseudoconstant) return; if (!is_opclause(clause)) return; if (list_length(((OpExpr *) clause)->args) != 2) return; opno = ((OpExpr *) clause)->opno; leftarg = linitial(((OpExpr *) clause)->args); if (op_hashjoinable(opno, exprType(leftarg)) && !contain_volatile_functions((Node *) clause)) restrictinfo->hashjoinoperator = opno; }
static void check_mergejoinable | ( | RestrictInfo * | restrictinfo | ) | [static] |
Definition at line 1860 of file initsplan.c.
References RestrictInfo::clause, contain_volatile_functions(), exprType(), get_mergejoin_opfamilies(), is_opclause, linitial, list_length(), RestrictInfo::mergeopfamilies, op_mergejoinable(), and RestrictInfo::pseudoconstant.
Referenced by build_implied_join_equality(), and distribute_qual_to_rels().
{ Expr *clause = restrictinfo->clause; Oid opno; Node *leftarg; if (restrictinfo->pseudoconstant) return; if (!is_opclause(clause)) return; if (list_length(((OpExpr *) clause)->args) != 2) return; opno = ((OpExpr *) clause)->opno; leftarg = linitial(((OpExpr *) clause)->args); if (op_mergejoinable(opno, exprType(leftarg)) && !contain_volatile_functions((Node *) clause)) restrictinfo->mergeopfamilies = get_mergejoin_opfamilies(opno); /* * Note: op_mergejoinable is just a hint; if we fail to find the operator * in any btree opfamilies, mergeopfamilies remains NIL and so the clause * is not treated as mergejoinable. */ }
static bool check_outerjoin_delay | ( | PlannerInfo * | root, | |
Relids * | relids_p, | |||
Relids * | nullable_relids_p, | |||
bool | is_pushed_down | |||
) | [static] |
Definition at line 1505 of file initsplan.c.
References bms_add_members(), bms_copy(), bms_free(), bms_int_members(), bms_is_subset(), bms_overlap(), SpecialJoinInfo::delay_upper_joins, JOIN_FULL, PlannerInfo::join_info_list, SpecialJoinInfo::jointype, lfirst, SpecialJoinInfo::min_lefthand, SpecialJoinInfo::min_righthand, and NIL.
Referenced by check_equivalence_delay(), and distribute_qual_to_rels().
{ Relids relids; Relids nullable_relids; bool outerjoin_delayed; bool found_some; /* fast path if no special joins */ if (root->join_info_list == NIL) { *nullable_relids_p = NULL; return false; } /* must copy relids because we need the original value at the end */ relids = bms_copy(*relids_p); nullable_relids = NULL; outerjoin_delayed = false; do { ListCell *l; found_some = false; foreach(l, root->join_info_list) { SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l); /* do we reference any nullable rels of this OJ? */ if (bms_overlap(relids, sjinfo->min_righthand) || (sjinfo->jointype == JOIN_FULL && bms_overlap(relids, sjinfo->min_lefthand))) { /* yes; have we included all its rels in relids? */ if (!bms_is_subset(sjinfo->min_lefthand, relids) || !bms_is_subset(sjinfo->min_righthand, relids)) { /* no, so add them in */ relids = bms_add_members(relids, sjinfo->min_lefthand); relids = bms_add_members(relids, sjinfo->min_righthand); outerjoin_delayed = true; /* we'll need another iteration */ found_some = true; } /* track all the nullable rels of relevant OJs */ nullable_relids = bms_add_members(nullable_relids, sjinfo->min_righthand); if (sjinfo->jointype == JOIN_FULL) nullable_relids = bms_add_members(nullable_relids, sjinfo->min_lefthand); /* set delay_upper_joins if needed */ if (is_pushed_down && sjinfo->jointype != JOIN_FULL && bms_overlap(relids, sjinfo->min_lefthand)) sjinfo->delay_upper_joins = true; } } } while (found_some); /* identify just the actually-referenced nullable rels */ nullable_relids = bms_int_members(nullable_relids, *relids_p); /* replace *relids_p, and return nullable_relids */ bms_free(*relids_p); *relids_p = relids; *nullable_relids_p = nullable_relids; return outerjoin_delayed; }
static bool check_redundant_nullability_qual | ( | PlannerInfo * | root, | |
Node * | clause | |||
) | [static] |
Definition at line 1624 of file initsplan.c.
References bms_is_member(), find_forced_null_var(), JOIN_ANTI, PlannerInfo::join_info_list, SpecialJoinInfo::jointype, lfirst, NULL, SpecialJoinInfo::syn_righthand, and Var::varno.
Referenced by distribute_qual_to_rels().
{ Var *forced_null_var; Index forced_null_rel; ListCell *lc; /* Check for IS NULL, and identify the Var forced to NULL */ forced_null_var = find_forced_null_var(clause); if (forced_null_var == NULL) return false; forced_null_rel = forced_null_var->varno; /* * If the Var comes from the nullable side of a lower antijoin, the IS * NULL condition is necessarily true. */ foreach(lc, root->join_info_list) { SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc); if (sjinfo->jointype == JOIN_ANTI && bms_is_member(forced_null_rel, sjinfo->syn_righthand)) return true; } return false; }
void create_lateral_join_info | ( | PlannerInfo * | root | ) |
Definition at line 377 of file initsplan.c.
References add_lateral_info(), PlannerInfo::append_rel_list, Assert, bms_add_member(), bms_add_members(), bms_copy(), bms_is_empty(), bms_make_singleton(), AppendRelInfo::child_relid, find_placeholder_info(), PlannerInfo::hasLateralRTEs, RangeTblEntry::inh, IsA, RelOptInfo::lateral_relids, RelOptInfo::lateral_vars, lfirst, NULL, AppendRelInfo::parent_relid, PlaceHolderInfo::ph_eval_at, RelOptInfo::relid, RELOPT_BASEREL, RELOPT_OTHER_MEMBER_REL, RelOptInfo::reloptkind, PlannerInfo::simple_rel_array, PlannerInfo::simple_rel_array_size, PlannerInfo::simple_rte_array, RangeQueryClause::var, and Var::varno.
Referenced by query_planner().
{ Index rti; /* We need do nothing if the query contains no LATERAL RTEs */ if (!root->hasLateralRTEs) return; /* * Examine all baserels (the rel array has been set up by now). */ for (rti = 1; rti < root->simple_rel_array_size; rti++) { RelOptInfo *brel = root->simple_rel_array[rti]; Relids lateral_relids; ListCell *lc; /* there may be empty slots corresponding to non-baserel RTEs */ if (brel == NULL) continue; Assert(brel->relid == rti); /* sanity check on array */ /* ignore RTEs that are "other rels" */ if (brel->reloptkind != RELOPT_BASEREL) continue; lateral_relids = NULL; /* consider each laterally-referenced Var or PHV */ foreach(lc, brel->lateral_vars) { Node *node = (Node *) lfirst(lc); if (IsA(node, Var)) { Var *var = (Var *) node; add_lateral_info(root, rti, bms_make_singleton(var->varno)); lateral_relids = bms_add_member(lateral_relids, var->varno); } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, false); add_lateral_info(root, rti, bms_copy(phinfo->ph_eval_at)); lateral_relids = bms_add_members(lateral_relids, phinfo->ph_eval_at); } else Assert(false); } /* We now know all the relids needed for lateral refs in this rel */ if (bms_is_empty(lateral_relids)) continue; /* ensure lateral_relids is NULL if empty */ brel->lateral_relids = lateral_relids; /* * If it's an appendrel parent, copy its lateral_relids to each child * rel. We intentionally give each child rel the same minimum * parameterization, even though it's quite possible that some don't * reference all the lateral rels. This is because any append path * for the parent will have to have the same parameterization for * every child anyway, and there's no value in forcing extra * reparameterize_path() calls. */ if (root->simple_rte_array[rti]->inh) { foreach(lc, root->append_rel_list) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc); RelOptInfo *childrel; if (appinfo->parent_relid != rti) continue; childrel = root->simple_rel_array[appinfo->child_relid]; Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); Assert(childrel->lateral_relids == NULL); childrel->lateral_relids = lateral_relids; } } } }
List* deconstruct_jointree | ( | PlannerInfo * | root | ) |
Definition at line 538 of file initsplan.c.
References Assert, deconstruct_recurse(), IsA, Query::jointree, NULL, and PlannerInfo::parse.
Referenced by query_planner().
static List * deconstruct_recurse | ( | PlannerInfo * | root, | |
Node * | jtnode, | |||
bool | below_outer_join, | |||
Relids * | qualscope, | |||
Relids * | inner_join_rels | |||
) | [static] |
Definition at line 571 of file initsplan.c.
References bms_add_members(), bms_make_singleton(), bms_union(), distribute_qual_to_rels(), elog, ERROR, from_collapse_limit, FromExpr::fromlist, IsA, JOIN_ANTI, join_collapse_limit, JOIN_FULL, PlannerInfo::join_info_list, JOIN_INNER, JOIN_LEFT, JOIN_SEMI, JoinExpr::jointype, lappend(), JoinExpr::larg, lfirst, linitial, list_concat(), list_length(), list_make1, list_make2, make_outerjoininfo(), SpecialJoinInfo::min_lefthand, SpecialJoinInfo::min_righthand, nodeTag, NULL, JoinExpr::quals, FromExpr::quals, JoinExpr::rarg, remaining, and update_placeholder_eval_levels().
Referenced by deconstruct_jointree().
{ List *joinlist; if (jtnode == NULL) { *qualscope = NULL; *inner_join_rels = NULL; return NIL; } if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; /* No quals to deal with, just return correct result */ *qualscope = bms_make_singleton(varno); /* A single baserel does not create an inner join */ *inner_join_rels = NULL; joinlist = list_make1(jtnode); } else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; int remaining; ListCell *l; /* * First, recurse to handle child joins. We collapse subproblems into * a single joinlist whenever the resulting joinlist wouldn't exceed * from_collapse_limit members. Also, always collapse one-element * subproblems, since that won't lengthen the joinlist anyway. */ *qualscope = NULL; *inner_join_rels = NULL; joinlist = NIL; remaining = list_length(f->fromlist); foreach(l, f->fromlist) { Relids sub_qualscope; List *sub_joinlist; int sub_members; sub_joinlist = deconstruct_recurse(root, lfirst(l), below_outer_join, &sub_qualscope, inner_join_rels); *qualscope = bms_add_members(*qualscope, sub_qualscope); sub_members = list_length(sub_joinlist); remaining--; if (sub_members <= 1 || list_length(joinlist) + sub_members + remaining <= from_collapse_limit) joinlist = list_concat(joinlist, sub_joinlist); else joinlist = lappend(joinlist, sub_joinlist); } /* * A FROM with more than one list element is an inner join subsuming * all below it, so we should report inner_join_rels = qualscope. If * there was exactly one element, we should (and already did) report * whatever its inner_join_rels were. If there were no elements (is * that possible?) the initialization before the loop fixed it. */ if (list_length(f->fromlist) > 1) *inner_join_rels = *qualscope; /* * Now process the top-level quals. */ foreach(l, (List *) f->quals) { Node *qual = (Node *) lfirst(l); distribute_qual_to_rels(root, qual, false, below_outer_join, JOIN_INNER, *qualscope, NULL, NULL, NULL); } } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; Relids leftids, rightids, left_inners, right_inners, nonnullable_rels, ojscope; List *leftjoinlist, *rightjoinlist; SpecialJoinInfo *sjinfo; ListCell *l; /* * Order of operations here is subtle and critical. First we recurse * to handle sub-JOINs. Their join quals will be placed without * regard for whether this level is an outer join, which is correct. * Then we place our own join quals, which are restricted by lower * outer joins in any case, and are forced to this level if this is an * outer join and they mention the outer side. Finally, if this is an * outer join, we create a join_info_list entry for the join. This * will prevent quals above us in the join tree that use those rels * from being pushed down below this level. (It's okay for upper * quals to be pushed down to the outer side, however.) */ switch (j->jointype) { case JOIN_INNER: leftjoinlist = deconstruct_recurse(root, j->larg, below_outer_join, &leftids, &left_inners); rightjoinlist = deconstruct_recurse(root, j->rarg, below_outer_join, &rightids, &right_inners); *qualscope = bms_union(leftids, rightids); *inner_join_rels = *qualscope; /* Inner join adds no restrictions for quals */ nonnullable_rels = NULL; break; case JOIN_LEFT: case JOIN_ANTI: leftjoinlist = deconstruct_recurse(root, j->larg, below_outer_join, &leftids, &left_inners); rightjoinlist = deconstruct_recurse(root, j->rarg, true, &rightids, &right_inners); *qualscope = bms_union(leftids, rightids); *inner_join_rels = bms_union(left_inners, right_inners); nonnullable_rels = leftids; break; case JOIN_SEMI: leftjoinlist = deconstruct_recurse(root, j->larg, below_outer_join, &leftids, &left_inners); rightjoinlist = deconstruct_recurse(root, j->rarg, below_outer_join, &rightids, &right_inners); *qualscope = bms_union(leftids, rightids); *inner_join_rels = bms_union(left_inners, right_inners); /* Semi join adds no restrictions for quals */ nonnullable_rels = NULL; break; case JOIN_FULL: leftjoinlist = deconstruct_recurse(root, j->larg, true, &leftids, &left_inners); rightjoinlist = deconstruct_recurse(root, j->rarg, true, &rightids, &right_inners); *qualscope = bms_union(leftids, rightids); *inner_join_rels = bms_union(left_inners, right_inners); /* each side is both outer and inner */ nonnullable_rels = *qualscope; break; default: /* JOIN_RIGHT was eliminated during reduce_outer_joins() */ elog(ERROR, "unrecognized join type: %d", (int) j->jointype); nonnullable_rels = NULL; /* keep compiler quiet */ leftjoinlist = rightjoinlist = NIL; break; } /* * For an OJ, form the SpecialJoinInfo now, because we need the OJ's * semantic scope (ojscope) to pass to distribute_qual_to_rels. But * we mustn't add it to join_info_list just yet, because we don't want * distribute_qual_to_rels to think it is an outer join below us. * * Semijoins are a bit of a hybrid: we build a SpecialJoinInfo, but we * want ojscope = NULL for distribute_qual_to_rels. */ if (j->jointype != JOIN_INNER) { sjinfo = make_outerjoininfo(root, leftids, rightids, *inner_join_rels, j->jointype, (List *) j->quals); if (j->jointype == JOIN_SEMI) ojscope = NULL; else ojscope = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand); } else { sjinfo = NULL; ojscope = NULL; } /* Process the qual clauses */ foreach(l, (List *) j->quals) { Node *qual = (Node *) lfirst(l); distribute_qual_to_rels(root, qual, false, below_outer_join, j->jointype, *qualscope, ojscope, nonnullable_rels, NULL); } /* Now we can add the SpecialJoinInfo to join_info_list */ if (sjinfo) { root->join_info_list = lappend(root->join_info_list, sjinfo); /* Each time we do that, recheck placeholder eval levels */ update_placeholder_eval_levels(root, sjinfo); } /* * Finally, compute the output joinlist. We fold subproblems together * except at a FULL JOIN or where join_collapse_limit would be * exceeded. */ if (j->jointype == JOIN_FULL) { /* force the join order exactly at this node */ joinlist = list_make1(list_make2(leftjoinlist, rightjoinlist)); } else if (list_length(leftjoinlist) + list_length(rightjoinlist) <= join_collapse_limit) { /* OK to combine subproblems */ joinlist = list_concat(leftjoinlist, rightjoinlist); } else { /* can't combine, but needn't force join order above here */ Node *leftpart, *rightpart; /* avoid creating useless 1-element sublists */ if (list_length(leftjoinlist) == 1) leftpart = (Node *) linitial(leftjoinlist); else leftpart = (Node *) leftjoinlist; if (list_length(rightjoinlist) == 1) rightpart = (Node *) linitial(rightjoinlist); else rightpart = (Node *) rightjoinlist; joinlist = list_make2(leftpart, rightpart); } } else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); joinlist = NIL; /* keep compiler quiet */ } return joinlist; }
static void distribute_qual_to_rels | ( | PlannerInfo * | root, | |
Node * | clause, | |||
bool | is_deduced, | |||
bool | below_outer_join, | |||
JoinType | jointype, | |||
Relids | qualscope, | |||
Relids | ojscope, | |||
Relids | outerjoin_nonnullable, | |||
Relids | deduced_nullable_relids | |||
) | [static] |
Definition at line 1095 of file initsplan.c.
References add_vars_to_targetlist(), Assert, bms_copy(), bms_is_empty(), bms_is_subset(), bms_membership(), BMS_MULTIPLE, bms_overlap(), RestrictInfo::can_join, check_equivalence_delay(), check_mergejoinable(), check_outerjoin_delay(), check_redundant_nullability_qual(), contain_volatile_functions(), distribute_restrictinfo_to_rels(), elog, ERROR, PlannerInfo::full_join_clauses, get_relids_in_jointree(), PlannerInfo::hasLateralRTEs, PlannerInfo::hasPseudoConstantQuals, initialize_mergeclause_eclasses(), JOIN_FULL, Query::jointree, lappend(), PlannerInfo::left_join_clauses, RestrictInfo::left_relids, list_free(), make_restrictinfo(), RestrictInfo::mergeopfamilies, NULL, PlannerInfo::parse, process_equivalence(), pull_var_clause(), pull_varnos(), PVC_INCLUDE_PLACEHOLDERS, PVC_RECURSE_AGGREGATES, PlannerInfo::right_join_clauses, and RestrictInfo::right_relids.
Referenced by deconstruct_recurse(), and process_implied_equality().
{ Relids relids; bool is_pushed_down; bool outerjoin_delayed; bool pseudoconstant = false; bool maybe_equivalence; bool maybe_outer_join; Relids nullable_relids; RestrictInfo *restrictinfo; /* * Retrieve all relids mentioned within the clause. */ relids = pull_varnos(clause); /* * Normally relids is a subset of qualscope, and we like to check that * here as a crosscheck on the parser and rewriter. That need not be the * case when there are LATERAL RTEs, however: the clause could contain * references to rels outside its syntactic scope as a consequence of * pull-up of such references from a LATERAL subquery below it. So, only * check if the query contains no LATERAL RTEs. * * However, if it's an outer-join clause, we always insist that relids be * a subset of ojscope. This is safe because is_simple_subquery() * disallows pullup of LATERAL subqueries that could cause the restriction * to be violated. */ if (!root->hasLateralRTEs && !bms_is_subset(relids, qualscope)) elog(ERROR, "JOIN qualification cannot refer to other relations"); if (ojscope && !bms_is_subset(relids, ojscope)) elog(ERROR, "JOIN qualification cannot refer to other relations"); /* * If the clause is variable-free, our normal heuristic for pushing it * down to just the mentioned rels doesn't work, because there are none. * * If the clause is an outer-join clause, we must force it to the OJ's * semantic level to preserve semantics. * * Otherwise, when the clause contains volatile functions, we force it to * be evaluated at its original syntactic level. This preserves the * expected semantics. * * When the clause contains no volatile functions either, it is actually a * pseudoconstant clause that will not change value during any one * execution of the plan, and hence can be used as a one-time qual in a * gating Result plan node. We put such a clause into the regular * RestrictInfo lists for the moment, but eventually createplan.c will * pull it out and make a gating Result node immediately above whatever * plan node the pseudoconstant clause is assigned to. It's usually best * to put a gating node as high in the plan tree as possible. If we are * not below an outer join, we can actually push the pseudoconstant qual * all the way to the top of the tree. If we are below an outer join, we * leave the qual at its original syntactic level (we could push it up to * just below the outer join, but that seems more complex than it's * worth). */ if (bms_is_empty(relids)) { if (ojscope) { /* clause is attached to outer join, eval it there */ relids = bms_copy(ojscope); /* mustn't use as gating qual, so don't mark pseudoconstant */ } else { /* eval at original syntactic level */ relids = bms_copy(qualscope); if (!contain_volatile_functions(clause)) { /* mark as gating qual */ pseudoconstant = true; /* tell createplan.c to check for gating quals */ root->hasPseudoConstantQuals = true; /* if not below outer join, push it to top of tree */ if (!below_outer_join) { relids = get_relids_in_jointree((Node *) root->parse->jointree, false); qualscope = bms_copy(relids); } } } } /*---------- * Check to see if clause application must be delayed by outer-join * considerations. * * A word about is_pushed_down: we mark the qual as "pushed down" if * it is (potentially) applicable at a level different from its original * syntactic level. This flag is used to distinguish OUTER JOIN ON quals * from other quals pushed down to the same joinrel. The rules are: * WHERE quals and INNER JOIN quals: is_pushed_down = true. * Non-degenerate OUTER JOIN quals: is_pushed_down = false. * Degenerate OUTER JOIN quals: is_pushed_down = true. * A "degenerate" OUTER JOIN qual is one that doesn't mention the * non-nullable side, and hence can be pushed down into the nullable side * without changing the join result. It is correct to treat it as a * regular filter condition at the level where it is evaluated. * * Note: it is not immediately obvious that a simple boolean is enough * for this: if for some reason we were to attach a degenerate qual to * its original join level, it would need to be treated as an outer join * qual there. However, this cannot happen, because all the rels the * clause mentions must be in the outer join's min_righthand, therefore * the join it needs must be formed before the outer join; and we always * attach quals to the lowest level where they can be evaluated. But * if we were ever to re-introduce a mechanism for delaying evaluation * of "expensive" quals, this area would need work. *---------- */ if (is_deduced) { /* * If the qual came from implied-equality deduction, it should not be * outerjoin-delayed, else deducer blew it. But we can't check this * because the join_info_list may now contain OJs above where the qual * belongs. For the same reason, we must rely on caller to supply the * correct nullable_relids set. */ Assert(!ojscope); is_pushed_down = true; outerjoin_delayed = false; nullable_relids = deduced_nullable_relids; /* Don't feed it back for more deductions */ maybe_equivalence = false; maybe_outer_join = false; } else if (bms_overlap(relids, outerjoin_nonnullable)) { /* * The qual is attached to an outer join and mentions (some of the) * rels on the nonnullable side, so it's not degenerate. * * We can't use such a clause to deduce equivalence (the left and * right sides might be unequal above the join because one of them has * gone to NULL) ... but we might be able to use it for more limited * deductions, if it is mergejoinable. So consider adding it to the * lists of set-aside outer-join clauses. */ is_pushed_down = false; maybe_equivalence = false; maybe_outer_join = true; /* Check to see if must be delayed by lower outer join */ outerjoin_delayed = check_outerjoin_delay(root, &relids, &nullable_relids, false); /* * Now force the qual to be evaluated exactly at the level of joining * corresponding to the outer join. We cannot let it get pushed down * into the nonnullable side, since then we'd produce no output rows, * rather than the intended single null-extended row, for any * nonnullable-side rows failing the qual. * * (Do this step after calling check_outerjoin_delay, because that * trashes relids.) */ Assert(ojscope); relids = ojscope; Assert(!pseudoconstant); } else { /* * Normal qual clause or degenerate outer-join clause. Either way, we * can mark it as pushed-down. */ is_pushed_down = true; /* Check to see if must be delayed by lower outer join */ outerjoin_delayed = check_outerjoin_delay(root, &relids, &nullable_relids, true); if (outerjoin_delayed) { /* Should still be a subset of current scope ... */ Assert(root->hasLateralRTEs || bms_is_subset(relids, qualscope)); Assert(ojscope == NULL || bms_is_subset(relids, ojscope)); /* * Because application of the qual will be delayed by outer join, * we mustn't assume its vars are equal everywhere. */ maybe_equivalence = false; /* * It's possible that this is an IS NULL clause that's redundant * with a lower antijoin; if so we can just discard it. We need * not test in any of the other cases, because this will only be * possible for pushed-down, delayed clauses. */ if (check_redundant_nullability_qual(root, clause)) return; } else { /* * Qual is not delayed by any lower outer-join restriction, so we * can consider feeding it to the equivalence machinery. However, * if it's itself within an outer-join clause, treat it as though * it appeared below that outer join (note that we can only get * here when the clause references only nullable-side rels). */ maybe_equivalence = true; if (outerjoin_nonnullable != NULL) below_outer_join = true; } /* * Since it doesn't mention the LHS, it's certainly not useful as a * set-aside OJ clause, even if it's in an OJ. */ maybe_outer_join = false; } /* * Build the RestrictInfo node itself. */ restrictinfo = make_restrictinfo((Expr *) clause, is_pushed_down, outerjoin_delayed, pseudoconstant, relids, outerjoin_nonnullable, nullable_relids); /* * If it's a join clause (either naturally, or because delayed by * outer-join rules), add vars used in the clause to targetlists of their * relations, so that they will be emitted by the plan nodes that scan * those relations (else they won't be available at the join node!). * * Note: if the clause gets absorbed into an EquivalenceClass then this * may be unnecessary, but for now we have to do it to cover the case * where the EC becomes ec_broken and we end up reinserting the original * clauses into the plan. */ if (bms_membership(relids) == BMS_MULTIPLE) { List *vars = pull_var_clause(clause, PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); add_vars_to_targetlist(root, vars, relids, false); list_free(vars); } /* * We check "mergejoinability" of every clause, not only join clauses, * because we want to know about equivalences between vars of the same * relation, or between vars and consts. */ check_mergejoinable(restrictinfo); /* * If it is a true equivalence clause, send it to the EquivalenceClass * machinery. We do *not* attach it directly to any restriction or join * lists. The EC code will propagate it to the appropriate places later. * * If the clause has a mergejoinable operator and is not * outerjoin-delayed, yet isn't an equivalence because it is an outer-join * clause, the EC code may yet be able to do something with it. We add it * to appropriate lists for further consideration later. Specifically: * * If it is a left or right outer-join qualification that relates the two * sides of the outer join (no funny business like leftvar1 = leftvar2 + * rightvar), we add it to root->left_join_clauses or * root->right_join_clauses according to which side the nonnullable * variable appears on. * * If it is a full outer-join qualification, we add it to * root->full_join_clauses. (Ideally we'd discard cases that aren't * leftvar = rightvar, as we do for left/right joins, but this routine * doesn't have the info needed to do that; and the current usage of the * full_join_clauses list doesn't require that, so it's not currently * worth complicating this routine's API to make it possible.) * * If none of the above hold, pass it off to * distribute_restrictinfo_to_rels(). * * In all cases, it's important to initialize the left_ec and right_ec * fields of a mergejoinable clause, so that all possibly mergejoinable * expressions have representations in EquivalenceClasses. If * process_equivalence is successful, it will take care of that; * otherwise, we have to call initialize_mergeclause_eclasses to do it. */ if (restrictinfo->mergeopfamilies) { if (maybe_equivalence) { if (check_equivalence_delay(root, restrictinfo) && process_equivalence(root, restrictinfo, below_outer_join)) return; /* EC rejected it, so set left_ec/right_ec the hard way ... */ initialize_mergeclause_eclasses(root, restrictinfo); /* ... and fall through to distribute_restrictinfo_to_rels */ } else if (maybe_outer_join && restrictinfo->can_join) { /* we need to set up left_ec/right_ec the hard way */ initialize_mergeclause_eclasses(root, restrictinfo); /* now see if it should go to any outer-join lists */ if (bms_is_subset(restrictinfo->left_relids, outerjoin_nonnullable) && !bms_overlap(restrictinfo->right_relids, outerjoin_nonnullable)) { /* we have outervar = innervar */ root->left_join_clauses = lappend(root->left_join_clauses, restrictinfo); return; } if (bms_is_subset(restrictinfo->right_relids, outerjoin_nonnullable) && !bms_overlap(restrictinfo->left_relids, outerjoin_nonnullable)) { /* we have innervar = outervar */ root->right_join_clauses = lappend(root->right_join_clauses, restrictinfo); return; } if (jointype == JOIN_FULL) { /* FULL JOIN (above tests cannot match in this case) */ root->full_join_clauses = lappend(root->full_join_clauses, restrictinfo); return; } /* nope, so fall through to distribute_restrictinfo_to_rels */ } else { /* we still need to set up left_ec/right_ec */ initialize_mergeclause_eclasses(root, restrictinfo); } } /* No EC special case applies, so push it into the clause lists */ distribute_restrictinfo_to_rels(root, restrictinfo); }
void distribute_restrictinfo_to_rels | ( | PlannerInfo * | root, | |
RestrictInfo * | restrictinfo | |||
) |
Definition at line 1662 of file initsplan.c.
References add_join_clause_to_rels(), RelOptInfo::baserestrictinfo, bms_membership(), BMS_MULTIPLE, BMS_SINGLETON, bms_singleton_member(), check_hashjoinable(), elog, ERROR, find_base_rel(), lappend(), and RestrictInfo::required_relids.
Referenced by distribute_qual_to_rels(), generate_base_implied_equalities_broken(), generate_base_implied_equalities_const(), reconsider_outer_join_clauses(), and remove_rel_from_query().
{ Relids relids = restrictinfo->required_relids; RelOptInfo *rel; switch (bms_membership(relids)) { case BMS_SINGLETON: /* * There is only one relation participating in the clause, so it * is a restriction clause for that relation. */ rel = find_base_rel(root, bms_singleton_member(relids)); /* Add clause to rel's restriction list */ rel->baserestrictinfo = lappend(rel->baserestrictinfo, restrictinfo); break; case BMS_MULTIPLE: /* * The clause is a join clause, since there is more than one rel * in its relid set. */ /* * Check for hashjoinable operators. (We don't bother setting the * hashjoin info except in true join clauses.) */ check_hashjoinable(restrictinfo); /* * Add clause to the join lists of all the relevant relations. */ add_join_clause_to_rels(root, restrictinfo, relids); break; default: /* * clause references no rels, and therefore we have no place to * attach it. Shouldn't get here if callers are working properly. */ elog(ERROR, "cannot cope with variable-free clause"); break; } }
static void extract_lateral_references | ( | PlannerInfo * | root, | |
RelOptInfo * | brel, | |||
Index | rtindex | |||
) | [static] |
Definition at line 286 of file initsplan.c.
References add_vars_to_targetlist(), Assert, bms_make_singleton(), copyObject(), RangeTblEntry::funcexpr, IncrementVarSublevelsUp(), IsA, lappend(), RangeTblEntry::lateral, RelOptInfo::lateral_vars, lfirst, list_free(), NIL, PlaceHolderVar::phexpr, PlaceHolderVar::phlevelsup, preprocess_phv_expression(), pull_vars_of_level(), RTE_FUNCTION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, PlannerInfo::simple_rte_array, RangeTblEntry::subquery, RangeTblEntry::values_lists, RangeQueryClause::var, and Var::varlevelsup.
Referenced by find_lateral_references().
{ RangeTblEntry *rte = root->simple_rte_array[rtindex]; List *vars; List *newvars; Relids where_needed; ListCell *lc; /* No cross-references are possible if it's not LATERAL */ if (!rte->lateral) return; /* Fetch the appropriate variables */ if (rte->rtekind == RTE_SUBQUERY) vars = pull_vars_of_level((Node *) rte->subquery, 1); else if (rte->rtekind == RTE_FUNCTION) vars = pull_vars_of_level(rte->funcexpr, 0); else if (rte->rtekind == RTE_VALUES) vars = pull_vars_of_level((Node *) rte->values_lists, 0); else { Assert(false); return; /* keep compiler quiet */ } if (vars == NIL) return; /* nothing to do */ /* Copy each Var (or PlaceHolderVar) and adjust it to match our level */ newvars = NIL; foreach(lc, vars) { Node *node = (Node *) lfirst(lc); node = copyObject(node); if (IsA(node, Var)) { Var *var = (Var *) node; /* Adjustment is easy since it's just one node */ var->varlevelsup = 0; } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; int levelsup = phv->phlevelsup; /* Have to work harder to adjust the contained expression too */ if (levelsup != 0) IncrementVarSublevelsUp(node, -levelsup, 0); /* * If we pulled the PHV out of a subquery RTE, its expression * needs to be preprocessed. subquery_planner() already did this * for level-zero PHVs in function and values RTEs, though. */ if (levelsup > 0) phv->phexpr = preprocess_phv_expression(root, phv->phexpr); } else Assert(false); newvars = lappend(newvars, node); } list_free(vars); /* * We mark the Vars as being "needed" at the LATERAL RTE. This is a bit * of a cheat: a more formal approach would be to mark each one as needed * at the join of the LATERAL RTE with its source RTE. But it will work, * and it's much less tedious than computing a separate where_needed for * each Var. */ where_needed = bms_make_singleton(rtindex); /* Push the Vars into their source relations' targetlists */ add_vars_to_targetlist(root, newvars, where_needed, true); /* Remember the lateral references for create_lateral_join_info */ brel->lateral_vars = newvars; }
void find_lateral_references | ( | PlannerInfo * | root | ) |
Definition at line 240 of file initsplan.c.
References Assert, extract_lateral_references(), PlannerInfo::hasLateralRTEs, NULL, RelOptInfo::relid, RELOPT_BASEREL, RelOptInfo::reloptkind, PlannerInfo::simple_rel_array, and PlannerInfo::simple_rel_array_size.
Referenced by query_planner().
{ Index rti; /* We need do nothing if the query contains no LATERAL RTEs */ if (!root->hasLateralRTEs) return; /* * Examine all baserels (the rel array has been set up by now). */ for (rti = 1; rti < root->simple_rel_array_size; rti++) { RelOptInfo *brel = root->simple_rel_array[rti]; /* there may be empty slots corresponding to non-baserel RTEs */ if (brel == NULL) continue; Assert(brel->relid == rti); /* sanity check on array */ /* * This bit is less obvious than it might look. We ignore appendrel * otherrels and consider only their parent baserels. In a case where * a LATERAL-containing UNION ALL subquery was pulled up, it is the * otherrels that are actually going to be in the plan. However, we * want to mark all their lateral references as needed by the parent, * because it is the parent's relid that will be used for join * planning purposes. And the parent's RTE will contain all the * lateral references we need to know, since the pulled-up members are * nothing but copies of parts of the original RTE's subquery. We * could visit the children instead and transform their references * back to the parent's relid, but it would be much more complicated * for no real gain. (Important here is that the child members have * not yet received any processing beyond being pulled up.) */ /* ignore RTEs that are "other rels" */ if (brel->reloptkind != RELOPT_BASEREL) continue; extract_lateral_references(root, brel, rti); } }
static SpecialJoinInfo * make_outerjoininfo | ( | PlannerInfo * | root, | |
Relids | left_rels, | |||
Relids | right_rels, | |||
Relids | inner_join_rels, | |||
JoinType | jointype, | |||
List * | clause | |||
) | [static] |
Definition at line 844 of file initsplan.c.
References Assert, bms_add_members(), bms_copy(), bms_int_members(), bms_intersect(), bms_is_empty(), bms_is_member(), bms_is_subset(), bms_overlap(), bms_union(), SpecialJoinInfo::delay_upper_joins, ereport, errcode(), errmsg(), ERROR, find_nonnullable_rels(), JOIN_ANTI, JOIN_FULL, PlannerInfo::join_info_list, JOIN_INNER, SpecialJoinInfo::join_quals, JOIN_RIGHT, JOIN_SEMI, SpecialJoinInfo::jointype, lfirst, SpecialJoinInfo::lhs_strict, makeNode, SpecialJoinInfo::min_lefthand, SpecialJoinInfo::min_righthand, PlannerInfo::parse, PlaceHolderInfo::ph_eval_at, PlaceHolderInfo::ph_may_need, PlaceHolderInfo::ph_var, PlaceHolderVar::phrels, PlannerInfo::placeholder_list, pull_varnos(), Query::rowMarks, RowMarkClause::rti, SpecialJoinInfo::syn_lefthand, and SpecialJoinInfo::syn_righthand.
Referenced by deconstruct_recurse().
{ SpecialJoinInfo *sjinfo = makeNode(SpecialJoinInfo); Relids clause_relids; Relids strict_relids; Relids min_lefthand; Relids min_righthand; ListCell *l; /* * We should not see RIGHT JOIN here because left/right were switched * earlier */ Assert(jointype != JOIN_INNER); Assert(jointype != JOIN_RIGHT); /* * Presently the executor cannot support FOR [KEY] UPDATE/SHARE marking of rels * appearing on the nullable side of an outer join. (It's somewhat unclear * what that would mean, anyway: what should we mark when a result row is * generated from no element of the nullable relation?) So, complain if * any nullable rel is FOR [KEY] UPDATE/SHARE. * * You might be wondering why this test isn't made far upstream in the * parser. It's because the parser hasn't got enough info --- consider * FOR UPDATE applied to a view. Only after rewriting and flattening do * we know whether the view contains an outer join. * * We use the original RowMarkClause list here; the PlanRowMark list would * list everything. */ foreach(l, root->parse->rowMarks) { RowMarkClause *rc = (RowMarkClause *) lfirst(l); if (bms_is_member(rc->rti, right_rels) || (jointype == JOIN_FULL && bms_is_member(rc->rti, left_rels))) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks cannot be applied to the nullable side of an outer join"))); } sjinfo->syn_lefthand = left_rels; sjinfo->syn_righthand = right_rels; sjinfo->jointype = jointype; /* this always starts out false */ sjinfo->delay_upper_joins = false; sjinfo->join_quals = clause; /* If it's a full join, no need to be very smart */ if (jointype == JOIN_FULL) { sjinfo->min_lefthand = bms_copy(left_rels); sjinfo->min_righthand = bms_copy(right_rels); sjinfo->lhs_strict = false; /* don't care about this */ return sjinfo; } /* * Retrieve all relids mentioned within the join clause. */ clause_relids = pull_varnos((Node *) clause); /* * For which relids is the clause strict, ie, it cannot succeed if the * rel's columns are all NULL? */ strict_relids = find_nonnullable_rels((Node *) clause); /* Remember whether the clause is strict for any LHS relations */ sjinfo->lhs_strict = bms_overlap(strict_relids, left_rels); /* * Required LHS always includes the LHS rels mentioned in the clause. We * may have to add more rels based on lower outer joins; see below. */ min_lefthand = bms_intersect(clause_relids, left_rels); /* * Similarly for required RHS. But here, we must also include any lower * inner joins, to ensure we don't try to commute with any of them. */ min_righthand = bms_int_members(bms_union(clause_relids, inner_join_rels), right_rels); foreach(l, root->join_info_list) { SpecialJoinInfo *otherinfo = (SpecialJoinInfo *) lfirst(l); /* ignore full joins --- other mechanisms preserve their ordering */ if (otherinfo->jointype == JOIN_FULL) continue; /* * For a lower OJ in our LHS, if our join condition uses the lower * join's RHS and is not strict for that rel, we must preserve the * ordering of the two OJs, so add lower OJ's full syntactic relset to * min_lefthand. (We must use its full syntactic relset, not just its * min_lefthand + min_righthand. This is because there might be other * OJs below this one that this one can commute with, but we cannot * commute with them if we don't with this one.) Also, if the current * join is a semijoin or antijoin, we must preserve ordering * regardless of strictness. * * Note: I believe we have to insist on being strict for at least one * rel in the lower OJ's min_righthand, not its whole syn_righthand. */ if (bms_overlap(left_rels, otherinfo->syn_righthand)) { if (bms_overlap(clause_relids, otherinfo->syn_righthand) && (jointype == JOIN_SEMI || jointype == JOIN_ANTI || !bms_overlap(strict_relids, otherinfo->min_righthand))) { min_lefthand = bms_add_members(min_lefthand, otherinfo->syn_lefthand); min_lefthand = bms_add_members(min_lefthand, otherinfo->syn_righthand); } } /* * For a lower OJ in our RHS, if our join condition does not use the * lower join's RHS and the lower OJ's join condition is strict, we * can interchange the ordering of the two OJs; otherwise we must add * lower OJ's full syntactic relset to min_righthand. Here, we must * preserve ordering anyway if either the current join is a semijoin, * or the lower OJ is either a semijoin or an antijoin. * * Here, we have to consider that "our join condition" includes any * clauses that syntactically appeared above the lower OJ and below * ours; those are equivalent to degenerate clauses in our OJ and must * be treated as such. Such clauses obviously can't reference our * LHS, and they must be non-strict for the lower OJ's RHS (else * reduce_outer_joins would have reduced the lower OJ to a plain * join). Hence the other ways in which we handle clauses within our * join condition are not affected by them. The net effect is * therefore sufficiently represented by the delay_upper_joins flag * saved for us by check_outerjoin_delay. */ if (bms_overlap(right_rels, otherinfo->syn_righthand)) { if (bms_overlap(clause_relids, otherinfo->syn_righthand) || jointype == JOIN_SEMI || otherinfo->jointype == JOIN_SEMI || otherinfo->jointype == JOIN_ANTI || !otherinfo->lhs_strict || otherinfo->delay_upper_joins) { min_righthand = bms_add_members(min_righthand, otherinfo->syn_lefthand); min_righthand = bms_add_members(min_righthand, otherinfo->syn_righthand); } } } /* * Examine PlaceHolderVars. If a PHV is supposed to be evaluated within * this join's nullable side, and it may get used above this join, then * ensure that min_righthand contains the full eval_at set of the PHV. * This ensures that the PHV actually can be evaluated within the RHS. * Note that this works only because we should already have determined the * final eval_at level for any PHV syntactically within this join. */ foreach(l, root->placeholder_list) { PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l); Relids ph_syn_level = phinfo->ph_var->phrels; /* Ignore placeholder if it didn't syntactically come from RHS */ if (!bms_is_subset(ph_syn_level, right_rels)) continue; /* We can also ignore it if it's certainly not used above this join */ /* XXX this test is probably overly conservative */ if (bms_is_subset(phinfo->ph_may_need, min_righthand)) continue; /* Else, prevent join from being formed before we eval the PHV */ min_righthand = bms_add_members(min_righthand, phinfo->ph_eval_at); } /* * If we found nothing to put in min_lefthand, punt and make it the full * LHS, to avoid having an empty min_lefthand which will confuse later * processing. (We don't try to be smart about such cases, just correct.) * Likewise for min_righthand. */ if (bms_is_empty(min_lefthand)) min_lefthand = bms_copy(left_rels); if (bms_is_empty(min_righthand)) min_righthand = bms_copy(right_rels); /* Now they'd better be nonempty */ Assert(!bms_is_empty(min_lefthand)); Assert(!bms_is_empty(min_righthand)); /* Shouldn't overlap either */ Assert(!bms_overlap(min_lefthand, min_righthand)); sjinfo->min_lefthand = min_lefthand; sjinfo->min_righthand = min_righthand; return sjinfo; }
void process_implied_equality | ( | PlannerInfo * | root, | |
Oid | opno, | |||
Oid | collation, | |||
Expr * | item1, | |||
Expr * | item2, | |||
Relids | qualscope, | |||
Relids | nullable_relids, | |||
bool | below_outer_join, | |||
bool | both_const | |||
) |
Definition at line 1741 of file initsplan.c.
References Assert, BOOLOID, Const::constisnull, Const::consttype, Const::constvalue, copyObject(), DatumGetBool, distribute_qual_to_rels(), eval_const_expressions(), InvalidOid, IsA, JOIN_INNER, make_opclause(), and NULL.
Referenced by generate_base_implied_equalities_const(), and generate_base_implied_equalities_no_const().
{ Expr *clause; /* * Build the new clause. Copy to ensure it shares no substructure with * original (this is necessary in case there are subselects in there...) */ clause = make_opclause(opno, BOOLOID, /* opresulttype */ false, /* opretset */ (Expr *) copyObject(item1), (Expr *) copyObject(item2), InvalidOid, collation); /* If both constant, try to reduce to a boolean constant. */ if (both_const) { clause = (Expr *) eval_const_expressions(root, (Node *) clause); /* If we produced const TRUE, just drop the clause */ if (clause && IsA(clause, Const)) { Const *cclause = (Const *) clause; Assert(cclause->consttype == BOOLOID); if (!cclause->constisnull && DatumGetBool(cclause->constvalue)) return; } } /* * Push the new clause into all the appropriate restrictinfo lists. */ distribute_qual_to_rels(root, (Node *) clause, true, below_outer_join, JOIN_INNER, qualscope, NULL, NULL, nullable_relids); }
Definition at line 34 of file initsplan.c.
Referenced by deconstruct_recurse().
Definition at line 35 of file initsplan.c.
Referenced by deconstruct_recurse().