initsplan.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 974 行 · 第 1/3 页
C
974 行
* Presently the executor cannot support FOR UPDATE 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. * 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 (intMember(relno, root->rowMarks)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE 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 'RestrictInfo' or 'JoinInfo' field * (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 * 'ispusheddown': if TRUE, force the clause to be marked 'ispusheddown' * (this indicates the clause came from a FromExpr, not a JoinExpr) * 'isdeduced': TRUE if the qual came from implied-equality deduction * '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 * 'ispusheddown' will be TRUE. */static voiddistribute_qual_to_rels(Query *root, Node *clause, bool ispusheddown, bool isdeduced, Relids outerjoin_nonnullable, Relids qualscope){ RestrictInfo *restrictinfo = makeNode(RestrictInfo); RelOptInfo *rel; Relids relids; List *vars; bool can_be_equijoin; restrictinfo->clause = (Expr *) clause; restrictinfo->subclauseindices = NIL; restrictinfo->eval_cost.startup = -1; /* not computed until * needed */ restrictinfo->this_selec = -1; /* not computed until needed */ restrictinfo->left_relids = NULL; /* set below, if join clause */ restrictinfo->right_relids = NULL; restrictinfo->mergejoinoperator = InvalidOid; restrictinfo->left_sortop = InvalidOid; restrictinfo->right_sortop = InvalidOid; restrictinfo->left_pathkey = NIL; /* not computable yet */ restrictinfo->right_pathkey = NIL; restrictinfo->left_mergescansel = -1; /* not computed until * needed */ restrictinfo->right_mergescansel = -1; restrictinfo->hashjoinoperator = InvalidOid; restrictinfo->left_bucketsize = -1; /* not computed until needed */ restrictinfo->right_bucketsize = -1; /* * Retrieve all relids and vars contained within the clause. */ clause_get_relids_vars(clause, &relids, &vars); /* * 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 (isdeduced) { /* * If the qual came from implied-equality deduction, we can * evaluate the qual at its natural semantic level. It is not * affected by any outer-join rules (else we'd not have decided * the vars were equal). */ Assert(bms_equal(relids, qualscope)); can_be_equijoin = true; } 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. */ relids = qualscope; can_be_equijoin = false; } 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; 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); } bms_free(tmprelids); if (bms_is_subset(addrelids, relids)) { /* Qual is not affected by any outer-join restriction */ can_be_equijoin = true; } 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. */ can_be_equijoin = false; } bms_free(addrelids); } /* * 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". */ restrictinfo->ispusheddown = ispusheddown || !bms_equal(relids, qualscope); 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. */ if (can_be_equijoin) 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 (!isdeduced || !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. Set additional RestrictInfo fields for * joining. First, does it look like a normal join clause, * i.e., a binary operator relating expressions that come from * distinct relations? If so we might be able to use it in a * join algorithm. */ if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2) { Relids left_relids; Relids right_relids; left_relids = pull_varnos(get_leftop((Expr *) clause)); right_relids = pull_varnos(get_rightop((Expr *) clause)); if (!bms_is_empty(left_relids) && !bms_is_empty(right_relids) && !bms_overlap(left_relids, right_relids)) { restrictinfo->left_relids = left_relids; restrictinfo->right_relids = right_relids; } } /* * Now 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!). */ add_vars_to_targetlist(root, vars, 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; } /* * If the clause has a mergejoinable operator, and 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). Record the key * equivalence for future use. (We can skip this for a deduced * clause, since the keys are already known equivalent in that case.) */ if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid && !isdeduced) add_equijoined_keys(root, restrictinfo);}/* * process_implied_equality * Check to see whether we already have a restrictinfo item that says * item1 = item2, and create one if not; or if delete_it is true, * remove any such restrictinfo item. * * This processing is a consequence of transitivity of mergejoin equality: * if we have mergejoinable clauses A = B and B = C, we can deduce A = C * (where = is an appropriate mergejoinable operator). See path/pathkeys.c * for more details. */voidprocess_implied_equality(Query *root, Node *item1, Node *item2, Oid sortop1, Oid sortop2, Relids item1_relids, Relids item2_relids, bool delete_it){ Relids relids; BMS_Membership membership; RelOptInfo *rel1; List *restrictlist;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?