joinpath.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 874 行 · 第 1/2 页
C
874 行
{ /* * Always consider a nestloop join with this outer and * cheapest-total-cost inner. When appropriate, also consider * using the materialized form of the cheapest inner, the * cheapest-startup-cost inner path, and the best innerjoin * indexpath. */ add_path(joinrel, (Path *) create_nestloop_path(root, joinrel, jointype, outerpath, inner_cheapest_total, restrictlist, merge_pathkeys)); if (matpath != NULL) add_path(joinrel, (Path *) create_nestloop_path(root, joinrel, jointype, outerpath, matpath, restrictlist, merge_pathkeys)); if (inner_cheapest_startup != inner_cheapest_total) add_path(joinrel, (Path *) create_nestloop_path(root, joinrel, jointype, outerpath, inner_cheapest_startup, restrictlist, merge_pathkeys)); if (bestinnerjoin != NULL) add_path(joinrel, (Path *) create_nestloop_path(root, joinrel, jointype, outerpath, bestinnerjoin, restrictlist, merge_pathkeys)); } /* Can't do anything else if outer path needs to be unique'd */ if (save_jointype == JOIN_UNIQUE_OUTER) continue; /* Look for useful mergeclauses (if any) */ mergeclauses = find_mergeclauses_for_pathkeys(root, outerpath->pathkeys, mergeclause_list); /* * Done with this outer path if no chance for a mergejoin. * * Special corner case: for "x FULL JOIN y ON true", there will be * no join clauses at all. Ordinarily we'd generate a clauseless * nestloop path, but since mergejoin is our only join type that * supports FULL JOIN, it's necessary to generate a clauseless * mergejoin path instead. * * Unfortunately this can't easily be extended to handle the case * where there are joinclauses but none of them use mergejoinable * operators; nodeMergejoin.c can only do a full join correctly if * all the joinclauses are mergeclauses. */ if (mergeclauses == NIL) { if (jointype == JOIN_FULL && restrictlist == NIL) /* okay to try for mergejoin */ ; else continue; } if (useallclauses && length(mergeclauses) != length(mergeclause_list)) continue; /* Compute the required ordering of the inner path */ innersortkeys = make_pathkeys_for_mergeclauses(root, mergeclauses, innerrel); /* * Generate a mergejoin on the basis of sorting the cheapest * inner. Since a sort will be needed, only cheapest total cost * matters. (But create_mergejoin_path will do the right thing if * inner_cheapest_total is already correctly sorted.) */ add_path(joinrel, (Path *) create_mergejoin_path(root, joinrel, jointype, outerpath, inner_cheapest_total, restrictlist, merge_pathkeys, mergeclauses, NIL, innersortkeys)); /* Can't do anything else if inner path needs to be unique'd */ if (save_jointype == JOIN_UNIQUE_INNER) continue; /* * Look for presorted inner paths that satisfy the innersortkey * list --- or any truncation thereof, if we are allowed to build * a mergejoin using a subset of the merge clauses. Here, we * consider both cheap startup cost and cheap total cost. Ignore * inner_cheapest_total, since we already made a path with it. */ num_sortkeys = length(innersortkeys); if (num_sortkeys > 1 && !useallclauses) trialsortkeys = listCopy(innersortkeys); /* need modifiable copy */ else trialsortkeys = innersortkeys; /* won't really truncate */ cheapest_startup_inner = NULL; cheapest_total_inner = NULL; for (sortkeycnt = num_sortkeys; sortkeycnt > 0; sortkeycnt--) { Path *innerpath; List *newclauses = NIL; /* * Look for an inner path ordered well enough for the first * 'sortkeycnt' innersortkeys. NB: trialsortkeys list is * modified destructively, which is why we made a copy... */ trialsortkeys = ltruncate(sortkeycnt, trialsortkeys); innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist, trialsortkeys, TOTAL_COST); if (innerpath != NULL && innerpath != inner_cheapest_total && (cheapest_total_inner == NULL || compare_path_costs(innerpath, cheapest_total_inner, TOTAL_COST) < 0)) { /* Found a cheap (or even-cheaper) sorted path */ /* Select the right mergeclauses, if we didn't already */ if (sortkeycnt < num_sortkeys) { newclauses = find_mergeclauses_for_pathkeys(root, trialsortkeys, mergeclauses); Assert(newclauses != NIL); } else newclauses = mergeclauses; add_path(joinrel, (Path *) create_mergejoin_path(root, joinrel, jointype, outerpath, innerpath, restrictlist, merge_pathkeys, newclauses, NIL, NIL)); cheapest_total_inner = innerpath; } /* Same on the basis of cheapest startup cost ... */ innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist, trialsortkeys, STARTUP_COST); if (innerpath != NULL && innerpath != inner_cheapest_total && (cheapest_startup_inner == NULL || compare_path_costs(innerpath, cheapest_startup_inner, STARTUP_COST) < 0)) { /* Found a cheap (or even-cheaper) sorted path */ if (innerpath != cheapest_total_inner) { /* * Avoid rebuilding clause list if we already made * one; saves memory in big join trees... */ if (newclauses == NIL) { if (sortkeycnt < num_sortkeys) { newclauses = find_mergeclauses_for_pathkeys(root, trialsortkeys, mergeclauses); Assert(newclauses != NIL); } else newclauses = mergeclauses; } add_path(joinrel, (Path *) create_mergejoin_path(root, joinrel, jointype, outerpath, innerpath, restrictlist, merge_pathkeys, newclauses, NIL, NIL)); } cheapest_startup_inner = innerpath; } /* * Don't consider truncated sortkeys if we need all clauses. */ if (useallclauses) 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(Query *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, JoinType jointype){ bool isouterjoin; List *hashclauses; List *i; /* * 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(i, restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); if (restrictinfo->left_relids == NULL || 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->ispusheddown) 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)); }}/* * select_mergejoin_clauses * Select mergejoin clauses that are usable for a particular join. * Returns a list of RestrictInfo nodes for those clauses. * * 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(RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, JoinType jointype){ List *result_list = NIL; bool isouterjoin = IS_OUTER_JOIN(jointype); List *i; foreach(i, restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); /* * If processing an outer join, only use its own join clauses in * the merge. For inner joins we need not be so picky. * * Furthermore, 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 (isouterjoin) { if (restrictinfo->ispusheddown) continue; switch (jointype) { case JOIN_RIGHT: if (restrictinfo->left_relids == NULL || restrictinfo->mergejoinoperator == InvalidOid) return NIL; /* not mergejoinable */ break; case JOIN_FULL: if (restrictinfo->left_relids == NULL || restrictinfo->mergejoinoperator == InvalidOid) 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; } } if (restrictinfo->left_relids == NULL || restrictinfo->mergejoinoperator == InvalidOid) 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 */ } 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 */ result_list = lcons(restrictinfo, result_list); } return result_list;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?