📄 initsplan.c
字号:
/* * Since we do this bottom-up, any outer-rels previously marked should * be within the new outer join set. */ Assert(bms_is_subset(rel->outerjoinset, outerrels)); /* * Presently the executor cannot support FOR 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 target rel is FOR UPDATE/SHARE. It's * sufficient to make this check once per rel, so do it only if rel * wasn't already known nullable. */ if (rel->outerjoinset == NULL) { if (list_member_int(root->parse->rowMarks, relno)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE/SHARE cannot be applied to the nullable side of an outer join"))); } rel->outerjoinset = outerrels; } bms_free(tmprelids);}/* * distribute_qual_to_rels * Add clause information to either the baserestrictinfo or joininfo list * (depending on whether the clause is a join) of each base relation * mentioned in the clause. A RestrictInfo node is created and added to * the appropriate list for each rel. Also, if the clause uses a * mergejoinable operator and is not delayed by outer-join rules, enter * the left- and right-side expressions into the query's lists of * equijoined vars. * * 'clause': the qual clause to be distributed * 'is_pushed_down': if TRUE, force the clause to be marked 'is_pushed_down' * (this indicates the clause came from a FromExpr, not a JoinExpr) * 'is_deduced': TRUE if the qual came from implied-equality deduction * 'below_outer_join': TRUE if the qual is from a JOIN/ON that is below the * nullable side of a higher-level outer join. * 'outerjoin_nonnullable': NULL if not an outer-join qual, else the set of * baserels appearing on the outer (nonnullable) side of the join * 'qualscope': set of baserels the qual's syntactic scope covers * * 'qualscope' identifies what level of JOIN the qual came from. For a top * level qual (WHERE qual), qualscope lists all baserel ids and in addition * 'is_pushed_down' will be TRUE. */static voiddistribute_qual_to_rels(PlannerInfo *root, Node *clause, bool is_pushed_down, bool is_deduced, bool below_outer_join, Relids outerjoin_nonnullable, Relids qualscope){ Relids relids; bool outerjoin_delayed; bool maybe_equijoin; bool maybe_outer_join; RestrictInfo *restrictinfo; RelOptInfo *rel; List *vars; /* * Retrieve all relids mentioned within the clause. */ relids = pull_varnos(clause); /* * Cross-check: clause should contain no relids not within its scope. * Otherwise the parser messed up. */ if (!bms_is_subset(relids, qualscope)) elog(ERROR, "JOIN qualification may not refer to other relations"); /* * If the clause is variable-free, we force it to be evaluated at its * original syntactic level. Note that this should not happen for * top-level clauses, because query_planner() special-cases them. But it * will happen for variable-free JOIN/ON clauses. We don't have to be * real smart about such a case, we just have to be correct. */ if (bms_is_empty(relids)) relids = qualscope; /* * Check to see if clause application must be delayed by outer-join * considerations. */ if (is_deduced) { /* * If the qual came from implied-equality deduction, we always * evaluate the qual at its natural semantic level. It is the * responsibility of the deducer not to create any quals that should * be delayed by outer-join rules. */ Assert(bms_equal(relids, qualscope)); /* Needn't feed it back for more deductions */ outerjoin_delayed = false; maybe_equijoin = 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. 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. * * Note: an outer-join qual that mentions only nullable-side rels can * be pushed down into the nullable side without changing the join * result, so we treat it the same as an ordinary inner-join qual, * except for not setting maybe_equijoin (see below). */ relids = qualscope; outerjoin_delayed = true; /* * We can't use such a clause to deduce equijoin (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 * purposes. Note: for the current uses of deductions from an * outer-join clause, it seems safe to make the deductions even when * the clause is below a higher-level outer join; so we do not check * below_outer_join here. */ maybe_equijoin = false; maybe_outer_join = true; } else { /* * For a non-outer-join qual, we can evaluate the qual as soon as (1) * we have all the rels it mentions, and (2) we are at or above any * outer joins that can null any of these rels and are below the * syntactic location of the given qual. To enforce the latter, scan * the base rels listed in relids, and merge their outer-join sets * into the clause's own reference list. At the time we are called, * the outerjoinset of each baserel will show exactly those outer * joins that are below the qual in the join tree. */ Relids addrelids = NULL; Relids tmprelids; int relno; outerjoin_delayed = false; tmprelids = bms_copy(relids); while ((relno = bms_first_member(tmprelids)) >= 0) { RelOptInfo *rel = find_base_rel(root, relno); if (rel->outerjoinset != NULL) { addrelids = bms_add_members(addrelids, rel->outerjoinset); outerjoin_delayed = true; } } bms_free(tmprelids); if (bms_is_subset(addrelids, relids)) { /* * Qual is not delayed by any lower outer-join restriction. If it * is not itself below or within an outer join, we can consider it * "valid everywhere", so consider feeding it to the equijoin * machinery. (If it is within an outer join, we can't consider * it "valid everywhere": once the contained variables have gone * to NULL, we'd be asserting things like NULL = NULL, which is * not true.) */ if (!below_outer_join && outerjoin_nonnullable == NULL) maybe_equijoin = true; else maybe_equijoin = false; } else { relids = bms_union(relids, addrelids); /* Should still be a subset of current scope ... */ Assert(bms_is_subset(relids, qualscope)); /* * Because application of the qual will be delayed by outer join, * we mustn't assume its vars are equal everywhere. */ maybe_equijoin = false; } bms_free(addrelids); maybe_outer_join = false; } /* * Mark the qual as "pushed down" if it can be applied at a level below * its original syntactic level. This allows us to distinguish original * JOIN/ON quals from higher-level quals pushed down to the same joinrel. * A qual originating from WHERE is always considered "pushed down". */ if (!is_pushed_down) is_pushed_down = !bms_equal(relids, qualscope); /* * Build the RestrictInfo node itself. */ restrictinfo = make_restrictinfo((Expr *) clause, is_pushed_down, outerjoin_delayed, relids); /* * Figure out where to attach it. */ switch (bms_membership(relids)) { case BMS_SINGLETON: /* * There is only one relation participating in 'clause', so * 'clause' is a restriction clause for that relation. */ rel = find_base_rel(root, bms_singleton_member(relids)); /* * Check for a "mergejoinable" clause even though it's not a join * clause. This is so that we can recognize that "a.x = a.y" * makes x and y eligible to be considered equal, even when they * belong to the same rel. Without this, we would not recognize * that "a.x = a.y AND a.x = b.z AND a.y = c.q" allows us to * consider z and q equal after their rels are joined. */ check_mergejoinable(restrictinfo); /* * If the clause was deduced from implied equality, check to see * whether it is redundant with restriction clauses we already * have for this rel. Note we cannot apply this check to * user-written clauses, since we haven't found the canonical * pathkey sets yet while processing user clauses. (NB: no * comparable check is done in the join-clause case; redundancy * will be detected when the join clause is moved into a join * rel's restriction list.) */ if (!is_deduced || !qual_is_redundant(root, restrictinfo, rel->baserestrictinfo)) { /* Add clause to rel's restriction list */ rel->baserestrictinfo = lappend(rel->baserestrictinfo, restrictinfo); } break; case BMS_MULTIPLE: /* * 'clause' is a join clause, since there is more than one rel in * the relid set. */ /* * Check for hash or mergejoinable operators. * * We don't bother setting the hashjoin info if we're not going to * need it. We do want to know about mergejoinable ops in all * cases, however, because we use mergejoinable ops for other * purposes such as detecting redundant clauses. */ check_mergejoinable(restrictinfo); if (enable_hashjoin) check_hashjoinable(restrictinfo); /* * Add clause to the join lists of all the relevant relations. */ add_join_clause_to_rels(root, restrictinfo, relids); /* * Add vars used in the join 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!). */ vars = pull_var_clause(clause, false); add_vars_to_targetlist(root, vars, relids); list_free(vars); 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; } /* * If the clause has a mergejoinable operator, we may be able to deduce * more things from it under the principle of transitivity. * * If it is not an outer-join qualification nor bubbled up due to an outer * join, then the two sides represent equivalent PathKeyItems for path * keys: any path that is sorted by one side will also be sorted by the * other (as soon as the two rels are joined, that is). Pass such clauses * to add_equijoined_keys. * * 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 (restrictinfo->mergejoinoperator != InvalidOid) { if (maybe_equijoin) add_equijoined_keys(root, restrictinfo); else if (maybe_outer_join && restrictinfo->can_join) { 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); } else 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); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -