📄 createplan.c
字号:
otherclauses, outer_plan, inner_plan, best_path->jointype); copy_path_costsize(&join_plan->join.plan, &best_path->path); return join_plan;}static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path, Plan *outer_plan, Plan *inner_plan){ List *tlist = build_relation_tlist(best_path->jpath.path.parent); List *joinclauses; List *otherclauses; List *mergeclauses; MergeJoin *join_plan; /* Get the join qual clauses (in plain expression form) */ if (IS_OUTER_JOIN(best_path->jpath.jointype)) { get_actual_join_clauses(best_path->jpath.joinrestrictinfo, &joinclauses, &otherclauses); } else { /* We can treat all clauses alike for an inner join */ joinclauses = get_actual_clauses(best_path->jpath.joinrestrictinfo); otherclauses = NIL; } /* * Remove the mergeclauses from the list of join qual clauses, leaving the * list of quals that must be checked as qpquals. */ mergeclauses = get_actual_clauses(best_path->path_mergeclauses); joinclauses = list_difference(joinclauses, mergeclauses); /* * Rearrange mergeclauses, if needed, so that the outer variable is always * on the left. */ mergeclauses = get_switched_clauses(best_path->path_mergeclauses, best_path->jpath.outerjoinpath->parent->relids); /* Sort clauses into best execution order */ /* NB: do NOT reorder the mergeclauses */ joinclauses = order_qual_clauses(root, joinclauses); otherclauses = order_qual_clauses(root, otherclauses); /* * Create explicit sort nodes for the outer and inner join paths if * necessary. The sort cost was already accounted for in the path. Make * sure there are no excess columns in the inputs if sorting. */ if (best_path->outersortkeys) { disuse_physical_tlist(outer_plan, best_path->jpath.outerjoinpath); outer_plan = (Plan *) make_sort_from_pathkeys(root, outer_plan, best_path->outersortkeys); } if (best_path->innersortkeys) { disuse_physical_tlist(inner_plan, best_path->jpath.innerjoinpath); inner_plan = (Plan *) make_sort_from_pathkeys(root, inner_plan, best_path->innersortkeys); } /* * Now we can build the mergejoin node. */ join_plan = make_mergejoin(tlist, joinclauses, otherclauses, mergeclauses, outer_plan, inner_plan, best_path->jpath.jointype); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); return join_plan;}static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path, Plan *outer_plan, Plan *inner_plan){ List *tlist = build_relation_tlist(best_path->jpath.path.parent); List *joinclauses; List *otherclauses; List *hashclauses; HashJoin *join_plan; Hash *hash_plan; /* Get the join qual clauses (in plain expression form) */ if (IS_OUTER_JOIN(best_path->jpath.jointype)) { get_actual_join_clauses(best_path->jpath.joinrestrictinfo, &joinclauses, &otherclauses); } else { /* We can treat all clauses alike for an inner join */ joinclauses = get_actual_clauses(best_path->jpath.joinrestrictinfo); otherclauses = NIL; } /* * Remove the hashclauses from the list of join qual clauses, leaving the * list of quals that must be checked as qpquals. */ hashclauses = get_actual_clauses(best_path->path_hashclauses); joinclauses = list_difference(joinclauses, hashclauses); /* * Rearrange hashclauses, if needed, so that the outer variable is always * on the left. */ hashclauses = get_switched_clauses(best_path->path_hashclauses, best_path->jpath.outerjoinpath->parent->relids); /* Sort clauses into best execution order */ joinclauses = order_qual_clauses(root, joinclauses); otherclauses = order_qual_clauses(root, otherclauses); hashclauses = order_qual_clauses(root, hashclauses); /* We don't want any excess columns in the hashed tuples */ disuse_physical_tlist(inner_plan, best_path->jpath.innerjoinpath); /* * Build the hash node and hash join node. */ hash_plan = make_hash(inner_plan); join_plan = make_hashjoin(tlist, joinclauses, otherclauses, hashclauses, outer_plan, (Plan *) hash_plan, best_path->jpath.jointype); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); return join_plan;}/***************************************************************************** * * SUPPORTING ROUTINES * *****************************************************************************//* * fix_indexqual_references * Adjust indexqual clauses to the form the executor's indexqual * machinery needs, and check for recheckable (lossy) index conditions. * * We have five tasks here: * * Remove RestrictInfo nodes from the input clauses. * * Index keys must be represented by Var nodes with varattno set to the * index's attribute number, not the attribute number in the original rel. * * If the index key is on the right, commute the clause to put it on the * left. * * We must construct lists of operator strategy numbers and subtypes * for the top-level operators of each index clause. * * We must detect any lossy index operators. The API is that we return * a list of the input clauses whose operators are NOT lossy. * * fixed_indexquals receives a modified copy of the indexquals list --- the * original is not changed. Note also that the copy shares no substructure * with the original; this is needed in case there is a subplan in it (we need * two separate copies of the subplan tree, or things will go awry). * * nonlossy_indexquals receives a list of the original input clauses (with * RestrictInfos) that contain non-lossy operators. * * indexstrategy receives an integer list of strategy numbers. * indexsubtype receives an OID list of strategy subtypes. */static voidfix_indexqual_references(List *indexquals, IndexPath *index_path, List **fixed_indexquals, List **nonlossy_indexquals, List **indexstrategy, List **indexsubtype){ IndexOptInfo *index = index_path->indexinfo; ListCell *l; *fixed_indexquals = NIL; *nonlossy_indexquals = NIL; *indexstrategy = NIL; *indexsubtype = NIL; /* * For each qual clause, commute if needed to put the indexkey operand on * the left, and then fix its varattno. (We do not need to change the * other side of the clause.) Then determine the operator's strategy * number and subtype number, and check for lossy index behavior. */ foreach(l, indexquals) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); OpExpr *clause; OpExpr *newclause; Oid opclass; int stratno; Oid stratsubtype; bool recheck; Assert(IsA(rinfo, RestrictInfo)); clause = (OpExpr *) rinfo->clause; if (!IsA(clause, OpExpr) || list_length(clause->args) != 2) elog(ERROR, "indexqual clause is not binary opclause"); /* * Make a copy that will become the fixed clause. * * We used to try to do a shallow copy here, but that fails if there * is a subplan in the arguments of the opclause. So just do a full * copy. */ newclause = (OpExpr *) copyObject((Node *) clause); /* * Check to see if the indexkey is on the right; if so, commute the * clause. The indexkey should be the side that refers to (only) the * base relation. */ if (!bms_equal(rinfo->left_relids, index->rel->relids)) CommuteClause(newclause); /* * Now, determine which index attribute this is, change the indexkey * operand as needed, and get the index opclass. */ linitial(newclause->args) = fix_indexqual_operand(linitial(newclause->args), index, &opclass); *fixed_indexquals = lappend(*fixed_indexquals, newclause); /* * Look up the (possibly commuted) operator in the operator class to * get its strategy numbers and the recheck indicator. This also * double-checks that we found an operator matching the index. */ get_op_opclass_properties(newclause->opno, opclass, &stratno, &stratsubtype, &recheck); *indexstrategy = lappend_int(*indexstrategy, stratno); *indexsubtype = lappend_oid(*indexsubtype, stratsubtype); /* If it's not lossy, add to nonlossy_indexquals */ if (!recheck) *nonlossy_indexquals = lappend(*nonlossy_indexquals, rinfo); }}static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, Oid *opclass){ /* * We represent index keys by Var nodes having the varno of the base table * but varattno equal to the index's attribute number (index column * position). This is a bit hokey ... would be cleaner to use a * special-purpose node type that could not be mistaken for a regular Var. * But it will do for now. */ Var *result; int pos; ListCell *indexpr_item; /* * Remove any binary-compatible relabeling of the indexkey */ if (IsA(node, RelabelType)) node = (Node *) ((RelabelType *) node)->arg; if (IsA(node, Var) && ((Var *) node)->varno == index->rel->relid) { /* Try to match against simple index columns */ int varatt = ((Var *) node)->varattno; if (varatt != 0) { for (pos = 0; pos < index->ncolumns; pos++) { if (index->indexkeys[pos] == varatt) { result = (Var *) copyObject(node); result->varattno = pos + 1; /* return the correct opclass, too */ *opclass = index->classlist[pos]; return (Node *) result; } } } } /* Try to match against index expressions */ indexpr_item = list_head(index->indexprs); for (pos = 0; pos < index->ncolumns; pos++) { if (index->indexkeys[pos] == 0) { Node *indexkey; if (indexpr_item == NULL) elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); if (indexkey && IsA(indexkey, RelabelType)) indexkey = (Node *) ((RelabelType *) indexkey)->arg; if (equal(node, indexkey)) { /* Found a match */ result = makeVar(index->rel->relid, pos + 1, exprType(lfirst(indexpr_item)), -1, 0); /* return the correct opclass, too */ *opclass = index->classlist[pos]; return (Node *) result; } indexpr_item = lnext(indexpr_item); } } /* Ooops... */ elog(ERROR, "node is not an index attribute"); *opclass = InvalidOid; /* keep compiler quiet */ return NULL;}/* * get_switched_clauses * Given a list of merge or hash joinclauses (as RestrictInfo nodes), * extract the bare clauses, and rearrange the elements within the * clauses, if needed, so the outer join variable is on the left and * the inner is on the right. The original data structure is not touched; * a modified list is returned. */static List *get_switched_clauses(List *clauses, Relids outerrelids){ List *t_list = NIL; ListCell *l; foreach(l, clauses) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l); OpExpr *clause = (OpExpr *) restrictinfo->clause; Assert(is_opclause(clause)); if (bms_is_subset(restrictinfo->right_relids, outerrelids)) { /* * Duplicate just enough of the structure to allow commuting the * clause without changing the original list. Could use * copyObject, but a complete deep copy is overkill. */ OpExpr *temp = makeNode(OpExpr); temp->opno = clause->opno; temp->opfuncid = InvalidOid; temp->opresulttype = clause->opresulttype; temp->opretset = clause->opretset; temp->args = list_copy(clause->args); /* Commute it --- note this modifies the temp node in-place. */ CommuteClause(temp); t_list = lappend(t_list, temp); } else t_list = lappend(t_list, clause); } return t_list;}/* * order_qual_clauses * Given a list of qual clauses that will all be evaluated at the same * plan node, sort the list into the order we want to check the quals * in at runtime. * * Ideally the order should be driven by a combination of execution cost and * selectivity, but unfortunately we have so little information about * execution cost of operators that it's really hard to do anything smart. * For now, we just move any quals that contain SubPlan references (but not * InitPlan references) to the end of the list. */List *order_qual_clauses(PlannerInfo *root, List *clauses){ List *nosubplans; List *withsubplans; ListCell *l; /* No need to work hard if the query is subselect-free */ if (!root->parse->hasSubLinks) return clauses; nosubplans = NIL; withsubplans = NIL; foreach(l, clauses) { Node *clause = (Node *) lfirst(l); if (contain_subplans(clause)) withsubplans = lappend(withsubplans, clause); else nosubplans = lappend(nosubplans, clause); } return list_concat(nosubplans, withsubplans);}/* * Copy cost and size info from a Path node to the Plan node created from it. * The executor won't use this info, but it's needed by EXPLAIN. */static voidcopy_path_costsize(Plan *dest, Path *src){ if (src) { dest->startup_cost = src->startup_cost; dest->total_cost = src->total_cost; dest->plan_rows = src->parent->rows; dest->plan_width = src->parent->width; } else { dest->startup_cost = 0; dest->total_cost = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -