equivclass.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,975 行 · 第 1/5 页
C
1,975 行
foreach(lc2, cur_ec->ec_members) { EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2); /* * If below an outer join, don't match constants: they're not as * constant as they look. */ if (cur_ec->ec_below_outer_join && cur_em->em_is_const) continue; if (expr_datatype == cur_em->em_datatype && equal(expr, cur_em->em_expr)) return cur_ec; /* Match! */ } } /* * No match, so build a new single-member EC * * Here, we must be sure that we construct the EC in the right context. We * can assume, however, that the passed expr is long-lived. */ oldcontext = MemoryContextSwitchTo(root->planner_cxt); newec = makeNode(EquivalenceClass); newec->ec_opfamilies = list_copy(opfamilies); newec->ec_members = NIL; newec->ec_sources = NIL; newec->ec_derives = NIL; newec->ec_relids = NULL; newec->ec_has_const = false; newec->ec_has_volatile = contain_volatile_functions((Node *) expr); newec->ec_below_outer_join = false; newec->ec_broken = false; newec->ec_sortref = sortref; newec->ec_merged = NULL; newem = add_eq_member(newec, expr, pull_varnos((Node *) expr), false, expr_datatype); /* * add_eq_member doesn't check for volatile functions, set-returning * functions, or aggregates, but such could appear in sort expressions; so * we have to check whether its const-marking was correct. */ if (newec->ec_has_const) { if (newec->ec_has_volatile || expression_returns_set((Node *) expr) || contain_agg_clause((Node *) expr)) { newec->ec_has_const = false; newem->em_is_const = false; } } root->eq_classes = lappend(root->eq_classes, newec); MemoryContextSwitchTo(oldcontext); return newec;}/* * generate_base_implied_equalities * Generate any restriction clauses that we can deduce from equivalence * classes. * * When an EC contains pseudoconstants, our strategy is to generate * "member = const1" clauses where const1 is the first constant member, for * every other member (including other constants). If we are able to do this * then we don't need any "var = var" comparisons because we've successfully * constrained all the vars at their points of creation. If we fail to * generate any of these clauses due to lack of cross-type operators, we fall * back to the "ec_broken" strategy described below. (XXX if there are * multiple constants of different types, it's possible that we might succeed * in forming all the required clauses if we started from a different const * member; but this seems a sufficiently hokey corner case to not be worth * spending lots of cycles on.) * * For ECs that contain no pseudoconstants, we generate derived clauses * "member1 = member2" for each pair of members belonging to the same base * relation (actually, if there are more than two for the same base relation, * we only need enough clauses to link each to each other). This provides * the base case for the recursion: each row emitted by a base relation scan * will constrain all computable members of the EC to be equal. As each * join path is formed, we'll add additional derived clauses on-the-fly * to maintain this invariant (see generate_join_implied_equalities). * * If the opfamilies used by the EC do not provide complete sets of cross-type * equality operators, it is possible that we will fail to generate a clause * that must be generated to maintain the invariant. (An example: given * "WHERE a.x = b.y AND b.y = a.z", the scheme breaks down if we cannot * generate "a.x = a.z" as a restriction clause for A.) In this case we mark * the EC "ec_broken" and fall back to regurgitating its original source * RestrictInfos at appropriate times. We do not try to retract any derived * clauses already generated from the broken EC, so the resulting plan could * be poor due to bad selectivity estimates caused by redundant clauses. But * the correct solution to that is to fix the opfamilies ... * * Equality clauses derived by this function are passed off to * process_implied_equality (in plan/initsplan.c) to be inserted into the * restrictinfo datastructures. Note that this must be called after initial * scanning of the quals and before Path construction begins. * * We make no attempt to avoid generating duplicate RestrictInfos here: we * don't search ec_sources for matches, nor put the created RestrictInfos * into ec_derives. Doing so would require some slightly ugly changes in * initsplan.c's API, and there's no real advantage, because the clauses * generated here can't duplicate anything we will generate for joins anyway. */voidgenerate_base_implied_equalities(PlannerInfo *root){ ListCell *lc; Index rti; foreach(lc, root->eq_classes) { EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc); Assert(ec->ec_merged == NULL); /* else shouldn't be in list */ Assert(!ec->ec_broken); /* not yet anyway... */ /* Single-member ECs won't generate any deductions */ if (list_length(ec->ec_members) <= 1) continue; if (ec->ec_has_const) generate_base_implied_equalities_const(root, ec); else generate_base_implied_equalities_no_const(root, ec); /* Recover if we failed to generate required derived clauses */ if (ec->ec_broken) generate_base_implied_equalities_broken(root, ec); } /* * This is also a handy place to mark base rels (which should all exist by * now) with flags showing whether they have pending eclass joins. */ for (rti = 1; rti < root->simple_rel_array_size; rti++) { RelOptInfo *brel = root->simple_rel_array[rti]; if (brel == NULL) continue; brel->has_eclass_joins = has_relevant_eclass_joinclause(root, brel); }}/* * generate_base_implied_equalities when EC contains pseudoconstant(s) */static voidgenerate_base_implied_equalities_const(PlannerInfo *root, EquivalenceClass *ec){ EquivalenceMember *const_em = NULL; ListCell *lc; /* * In the trivial case where we just had one "var = const" clause, * push the original clause back into the main planner machinery. There * is nothing to be gained by doing it differently, and we save the * effort to re-build and re-analyze an equality clause that will be * exactly equivalent to the old one. */ if (list_length(ec->ec_members) == 2 && list_length(ec->ec_sources) == 1) { RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources); if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE) { distribute_restrictinfo_to_rels(root, restrictinfo); return; } } /* Find the constant member to use */ foreach(lc, ec->ec_members) { EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc); if (cur_em->em_is_const) { const_em = cur_em; break; } } Assert(const_em != NULL); /* Generate a derived equality against each other member */ foreach(lc, ec->ec_members) { EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc); Oid eq_op; Assert(!cur_em->em_is_child); /* no children yet */ if (cur_em == const_em) continue; eq_op = select_equality_operator(ec, cur_em->em_datatype, const_em->em_datatype); if (!OidIsValid(eq_op)) { /* failed... */ ec->ec_broken = true; break; } process_implied_equality(root, eq_op, cur_em->em_expr, const_em->em_expr, ec->ec_relids, ec->ec_below_outer_join, cur_em->em_is_const); }}/* * generate_base_implied_equalities when EC contains no pseudoconstants */static voidgenerate_base_implied_equalities_no_const(PlannerInfo *root, EquivalenceClass *ec){ EquivalenceMember **prev_ems; ListCell *lc; /* * We scan the EC members once and track the last-seen member for each * base relation. When we see another member of the same base relation, * we generate "prev_mem = cur_mem". This results in the minimum number * of derived clauses, but it's possible that it will fail when a * different ordering would succeed. XXX FIXME: use a UNION-FIND * algorithm similar to the way we build merged ECs. (Use a list-of-lists * for each rel.) */ prev_ems = (EquivalenceMember **) palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *)); foreach(lc, ec->ec_members) { EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc); int relid; Assert(!cur_em->em_is_child); /* no children yet */ if (bms_membership(cur_em->em_relids) != BMS_SINGLETON) continue; relid = bms_singleton_member(cur_em->em_relids); Assert(relid < root->simple_rel_array_size); if (prev_ems[relid] != NULL) { EquivalenceMember *prev_em = prev_ems[relid]; Oid eq_op; eq_op = select_equality_operator(ec, prev_em->em_datatype, cur_em->em_datatype); if (!OidIsValid(eq_op)) { /* failed... */ ec->ec_broken = true; break; } process_implied_equality(root, eq_op, prev_em->em_expr, cur_em->em_expr, ec->ec_relids, ec->ec_below_outer_join, false); } prev_ems[relid] = cur_em; } pfree(prev_ems); /* * We also have to make sure that all the Vars used in the member clauses * will be available at any join node we might try to reference them at. * For the moment we force all the Vars to be available at all join nodes * for this eclass. Perhaps this could be improved by doing some * pre-analysis of which members we prefer to join, but it's no worse than * what happened in the pre-8.3 code. */ foreach(lc, ec->ec_members) { EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc); List *vars = pull_var_clause((Node *) cur_em->em_expr, false); add_vars_to_targetlist(root, vars, ec->ec_relids); list_free(vars); }}/* * generate_base_implied_equalities cleanup after failure * * What we must do here is push any zero- or one-relation source RestrictInfos * of the EC back into the main restrictinfo datastructures. Multi-relation * clauses will be regurgitated later by generate_join_implied_equalities(). * (We do it this way to maintain continuity with the case that ec_broken * becomes set only after we've gone up a join level or two.) */static voidgenerate_base_implied_equalities_broken(PlannerInfo *root, EquivalenceClass *ec){ ListCell *lc; foreach(lc, ec->ec_sources) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc); if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE) distribute_restrictinfo_to_rels(root, restrictinfo); }}/* * generate_join_implied_equalities * Generate any join clauses that we can deduce from equivalence classes. * * At a join node, we must enforce restriction clauses sufficient to ensure * that all equivalence-class members computable at that node are equal. * Since the set of clauses to enforce can vary depending on which subset * relations are the inputs, we have to compute this afresh for each join * path pair. Hence a fresh List of RestrictInfo nodes is built and passed * back on each call. * * The results are sufficient for use in merge, hash, and plain nestloop join * methods. We do not worry here about selecting clauses that are optimal * for use in a nestloop-with-inner-indexscan join, however. indxpath.c makes * its own selections of clauses to use, and if the ones we pick here are * redundant with those, the extras will be eliminated in createplan.c. * * Because the same join clauses are likely to be needed multiple times as * we consider different join paths, we avoid generating multiple copies: * whenever we select a particular pair of EquivalenceMembers to join, * we check to see if the pair matches any original clause (in ec_sources) * or previously-built clause (in ec_derives). This saves memory and allows * re-use of information cached in RestrictInfos. */List *generate_join_implied_equalities(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outer_rel, RelOptInfo *inner_rel){ List *result = NIL; ListCell *lc; foreach(lc, root->eq_classes) { EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc); List *sublist = NIL; /* ECs containing consts do not need any further enforcement */ if (ec->ec_has_const) continue; /* Single-member ECs won't generate any deductions */ if (list_length(ec->ec_members) <= 1) continue; /* We can quickly ignore any that don't overlap the join, too */ if (!bms_overlap(ec->ec_relids, joinrel->relids)) continue; if (!ec->ec_broken) sublist = generate_join_implied_equalities_normal(root, ec, joinrel, outer_rel, inner_rel); /* Recover if we failed to generate required derived clauses */ if (ec->ec_broken) sublist = generate_join_implied_equalities_broken(root, ec, joinrel, outer_rel, inner_rel); result = list_concat(result, sublist); } return result;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?