joinpath.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 999 行 · 第 1/3 页
C
999 行
break; } }}/* * hash_inner_and_outer * Create hashjoin join paths by explicitly hashing both the outer and * inner keys of each available hash clause. * * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation * 'restrictlist' contains all of the RestrictInfo nodes for restriction * clauses that apply to this join * 'jointype' is the type of join to do */static voidhash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, JoinType jointype){ bool isouterjoin; List *hashclauses; ListCell *l; /* * Hashjoin only supports inner, left, and IN joins. */ switch (jointype) { case JOIN_INNER: case JOIN_IN: case JOIN_UNIQUE_OUTER: case JOIN_UNIQUE_INNER: isouterjoin = false; break; case JOIN_LEFT: isouterjoin = true; break; default: return; } /* * We need to build only one hashpath for any given pair of outer and * inner relations; all of the hashable clauses will be used as keys. * * Scan the join's restrictinfo list to find hashjoinable clauses that are * usable with this pair of sub-relations. */ hashclauses = NIL; foreach(l, restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l); if (!restrictinfo->can_join || restrictinfo->hashjoinoperator == InvalidOid) continue; /* not hashjoinable */ /* * If processing an outer join, only use its own join clauses for * hashing. For inner joins we need not be so picky. */ if (isouterjoin && restrictinfo->is_pushed_down) continue; /* * Check if clause is usable with these input rels. */ if (bms_is_subset(restrictinfo->left_relids, outerrel->relids) && bms_is_subset(restrictinfo->right_relids, innerrel->relids)) { /* righthand side is inner */ } else if (bms_is_subset(restrictinfo->left_relids, innerrel->relids) && bms_is_subset(restrictinfo->right_relids, outerrel->relids)) { /* lefthand side is inner */ } else continue; /* no good for these input relations */ hashclauses = lappend(hashclauses, restrictinfo); } /* If we found any usable hashclauses, make a path */ if (hashclauses) { /* * We consider both the cheapest-total-cost and cheapest-startup-cost * outer paths. There's no need to consider any but the * cheapest-total-cost inner path, however. */ Path *cheapest_startup_outer = outerrel->cheapest_startup_path; Path *cheapest_total_outer = outerrel->cheapest_total_path; Path *cheapest_total_inner = innerrel->cheapest_total_path; /* Unique-ify if need be */ if (jointype == JOIN_UNIQUE_OUTER) { cheapest_total_outer = (Path *) create_unique_path(root, outerrel, cheapest_total_outer); cheapest_startup_outer = cheapest_total_outer; jointype = JOIN_INNER; } else if (jointype == JOIN_UNIQUE_INNER) { cheapest_total_inner = (Path *) create_unique_path(root, innerrel, cheapest_total_inner); jointype = JOIN_INNER; } add_path(joinrel, (Path *) create_hashjoin_path(root, joinrel, jointype, cheapest_total_outer, cheapest_total_inner, restrictlist, hashclauses)); if (cheapest_startup_outer != cheapest_total_outer) add_path(joinrel, (Path *) create_hashjoin_path(root, joinrel, jointype, cheapest_startup_outer, cheapest_total_inner, restrictlist, hashclauses)); }}/* * best_appendrel_indexscan * Finds the best available set of inner indexscans for a nestloop join * with the given append relation on the inside and the given outer_rel * outside. Returns an AppendPath comprising the best inner scans, or * NULL if there are no possible inner indexscans. * * Note that we currently consider only cheapest-total-cost. It's not * very clear what cheapest-startup-cost might mean for an AppendPath. */static Path *best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel, RelOptInfo *outer_rel, JoinType jointype){ int parentRTindex = rel->relid; List *append_paths = NIL; bool found_indexscan = false; ListCell *l; foreach(l, root->append_rel_list) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); int childRTindex; RelOptInfo *childrel; Path *index_cheapest_startup; Path *index_cheapest_total; /* append_rel_list contains all append rels; ignore others */ if (appinfo->parent_relid != parentRTindex) continue; childRTindex = appinfo->child_relid; childrel = find_base_rel(root, childRTindex); Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); /* * Check to see if child was rejected by constraint exclusion. If so, * it will have a cheapest_total_path that's a "dummy" path. */ if (IS_DUMMY_PATH(childrel->cheapest_total_path)) continue; /* OK, we can ignore it */ /* * Get the best innerjoin indexpaths (if any) for this child rel. */ best_inner_indexscan(root, childrel, outer_rel, jointype, &index_cheapest_startup, &index_cheapest_total); /* * If no luck on an indexpath for this rel, we'll still consider an * Append substituting the cheapest-total inner path. However we must * find at least one indexpath, else there's not going to be any * improvement over the base path for the appendrel. */ if (index_cheapest_total) found_indexscan = true; else index_cheapest_total = childrel->cheapest_total_path; append_paths = lappend(append_paths, index_cheapest_total); } if (!found_indexscan) return NULL; /* Form and return the completed Append path. */ return (Path *) create_append_path(rel, append_paths);}/* * select_mergejoin_clauses * Select mergejoin clauses that are usable for a particular join. * Returns a list of RestrictInfo nodes for those clauses. * * We also mark each selected RestrictInfo to show which side is currently * being considered as outer. These are transient markings that are only * good for the duration of the current add_paths_to_joinrel() call! * * We examine each restrictinfo clause known for the join to see * if it is mergejoinable and involves vars from the two sub-relations * currently of interest. */static List *select_mergejoin_clauses(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, JoinType jointype){ List *result_list = NIL; bool isouterjoin = IS_OUTER_JOIN(jointype); bool have_nonmergeable_joinclause = false; ListCell *l; foreach(l, restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l); /* * If processing an outer join, only use its own join clauses in the * merge. For inner joins we can use pushed-down clauses too. (Note: * we don't set have_nonmergeable_joinclause here because pushed-down * clauses will become otherquals not joinquals.) */ if (isouterjoin && restrictinfo->is_pushed_down) continue; if (!restrictinfo->can_join || restrictinfo->mergeopfamilies == NIL) { have_nonmergeable_joinclause = true; continue; /* not mergejoinable */ } /* * Check if clause is usable with these input rels. All the vars * needed on each side of the clause must be available from one or the * other of the input rels. */ if (bms_is_subset(restrictinfo->left_relids, outerrel->relids) && bms_is_subset(restrictinfo->right_relids, innerrel->relids)) { /* righthand side is inner */ restrictinfo->outer_is_left = true; } else if (bms_is_subset(restrictinfo->left_relids, innerrel->relids) && bms_is_subset(restrictinfo->right_relids, outerrel->relids)) { /* lefthand side is inner */ restrictinfo->outer_is_left = false; } else { have_nonmergeable_joinclause = true; continue; /* no good for these input relations */ } /* * Insist that each side have a non-redundant eclass. This * restriction is needed because various bits of the planner expect * that each clause in a merge be associatable with some pathkey in a * canonical pathkey list, but redundant eclasses can't appear in * canonical sort orderings. (XXX it might be worth relaxing this, * but not enough time to address it for 8.3.) * * Note: it would be bad if this condition failed for an otherwise * mergejoinable FULL JOIN clause, since that would result in * undesirable planner failure. I believe that is not possible * however; a variable involved in a full join could only appear * in below_outer_join eclasses, which aren't considered redundant. * * This case *can* happen for left/right join clauses: the * outer-side variable could be equated to a constant. Because we * will propagate that constant across the join clause, the loss of * ability to do a mergejoin is not really all that big a deal, and * so it's not clear that improving this is important. */ cache_mergeclause_eclasses(root, restrictinfo); if (EC_MUST_BE_REDUNDANT(restrictinfo->left_ec) || EC_MUST_BE_REDUNDANT(restrictinfo->right_ec)) { have_nonmergeable_joinclause = true; continue; /* can't handle redundant eclasses */ } result_list = lappend(result_list, restrictinfo); } /* * If it is a right/full join then *all* the explicit join clauses must be * mergejoinable, else the executor will fail. If we are asked for a right * join then just return NIL to indicate no mergejoin is possible (we can * handle it as a left join instead). If we are asked for a full join then * emit an error, because there is no fallback. */ if (have_nonmergeable_joinclause) { switch (jointype) { case JOIN_RIGHT: return NIL; /* not mergejoinable */ case JOIN_FULL: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("FULL JOIN is only supported with merge-joinable join conditions"))); break; default: /* otherwise, it's OK to have nonmergeable join quals */ break; } } return result_list;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?