📄 initsplan.c
字号:
else if (bms_equal(outerjoin_nonnullable, qualscope)) { /* FULL JOIN (above tests cannot match in this case) */ root->full_join_clauses = lappend(root->full_join_clauses, 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(PlannerInfo *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; ListCell *itm; Oid ltype, rtype; Operator eq_operator; Form_pg_operator pgopform; Expr *clause; /* Get set of relids referenced in the two expressions */ relids = bms_union(item1_relids, item2_relids); membership = bms_membership(relids); /* * generate_implied_equalities() shouldn't call me on two constants. */ Assert(membership != BMS_EMPTY_SET); /* * If the exprs involve a single rel, we need to look at that rel's * baserestrictinfo list. If multiple rels, we can scan the joininfo list * of any of 'em. */ if (membership == BMS_SINGLETON) { rel1 = find_base_rel(root, bms_singleton_member(relids)); restrictlist = rel1->baserestrictinfo; } else { Relids other_rels; int first_rel; /* Copy relids, find and remove one member */ other_rels = bms_copy(relids); first_rel = bms_first_member(other_rels); bms_free(other_rels); rel1 = find_base_rel(root, first_rel); restrictlist = rel1->joininfo; } /* * Scan to see if equality is already known. If so, we're done in the add * case, and done after removing it in the delete case. */ foreach(itm, restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm); Node *left, *right; if (restrictinfo->mergejoinoperator == InvalidOid) continue; /* ignore non-mergejoinable clauses */ /* We now know the restrictinfo clause is a binary opclause */ left = get_leftop(restrictinfo->clause); right = get_rightop(restrictinfo->clause); if ((equal(item1, left) && equal(item2, right)) || (equal(item2, left) && equal(item1, right))) { /* found a matching clause */ if (delete_it) { if (membership == BMS_SINGLETON) { /* delete it from local restrictinfo list */ rel1->baserestrictinfo = list_delete_ptr(rel1->baserestrictinfo, restrictinfo); } else { /* let joininfo.c do it */ remove_join_clause_from_rels(root, restrictinfo, relids); } } return; /* done */ } } /* Didn't find it. Done if deletion requested */ if (delete_it) return; /* * This equality is new information, so construct a clause representing it * to add to the query data structures. */ ltype = exprType(item1); rtype = exprType(item2); eq_operator = compatible_oper(list_make1(makeString("=")), ltype, rtype, true); if (!HeapTupleIsValid(eq_operator)) { /* * Would it be safe to just not add the equality to the query if we * have no suitable equality operator for the combination of * datatypes? NO, because sortkey selection may screw up anyway. */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify an equality operator for types %s and %s", format_type_be(ltype), format_type_be(rtype)))); } pgopform = (Form_pg_operator) GETSTRUCT(eq_operator); /* * Let's just make sure this appears to be a compatible operator. */ if (pgopform->oprlsortop != sortop1 || pgopform->oprrsortop != sortop2 || pgopform->oprresult != BOOLOID) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("equality operator for types %s and %s should be merge-joinable, but isn't", format_type_be(ltype), format_type_be(rtype)))); /* * Now we can build the new clause. Copy to ensure it shares no * substructure with original (this is necessary in case there are * subselects in there...) */ clause = make_opclause(oprid(eq_operator), /* opno */ BOOLOID, /* opresulttype */ false, /* opretset */ (Expr *) copyObject(item1), (Expr *) copyObject(item2)); ReleaseSysCache(eq_operator); /* * Push the new clause into all the appropriate restrictinfo lists. * * Note: we mark the qual "pushed down" to ensure that it can never be * taken for an original JOIN/ON clause. */ distribute_qual_to_rels(root, (Node *) clause, true, true, false, NULL, relids);}/* * qual_is_redundant * Detect whether an implied-equality qual that turns out to be a * restriction clause for a single base relation is redundant with * already-known restriction clauses for that rel. This occurs with, * for example, * SELECT * FROM tab WHERE f1 = f2 AND f2 = f3; * We need to suppress the redundant condition to avoid computing * too-small selectivity, not to mention wasting time at execution. * * Note: quals of the form "var = const" are never considered redundant, * only those of the form "var = var". This is needed because when we * have constants in an implied-equality set, we use a different strategy * that suppresses all "var = var" deductions. We must therefore keep * all the "var = const" quals. */static boolqual_is_redundant(PlannerInfo *root, RestrictInfo *restrictinfo, List *restrictlist){ Node *newleft; Node *newright; List *oldquals; ListCell *olditem; List *equalexprs; bool someadded; /* Never redundant unless vars appear on both sides */ if (bms_is_empty(restrictinfo->left_relids) || bms_is_empty(restrictinfo->right_relids)) return false; newleft = get_leftop(restrictinfo->clause); newright = get_rightop(restrictinfo->clause); /* * Set cached pathkeys. NB: it is okay to do this now because this * routine is only invoked while we are generating implied equalities. * Therefore, the equi_key_list is already complete and so we can * correctly determine canonical pathkeys. */ cache_mergeclause_pathkeys(root, restrictinfo); /* If different, say "not redundant" (should never happen) */ if (restrictinfo->left_pathkey != restrictinfo->right_pathkey) return false; /* * Scan existing quals to find those referencing same pathkeys. Usually * there will be few, if any, so build a list of just the interesting * ones. */ oldquals = NIL; foreach(olditem, restrictlist) { RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem); if (oldrinfo->mergejoinoperator != InvalidOid) { cache_mergeclause_pathkeys(root, oldrinfo); if (restrictinfo->left_pathkey == oldrinfo->left_pathkey && restrictinfo->right_pathkey == oldrinfo->right_pathkey) oldquals = lcons(oldrinfo, oldquals); } } if (oldquals == NIL) return false; /* * Now, we want to develop a list of exprs that are known equal to the * left side of the new qual. We traverse the old-quals list repeatedly * to transitively expand the exprs list. If at any point we find we can * reach the right-side expr of the new qual, we are done. We give up * when we can't expand the equalexprs list any more. */ equalexprs = list_make1(newleft); do { someadded = false; /* cannot use foreach here because of possible list_delete */ olditem = list_head(oldquals); while (olditem) { RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem); Node *oldleft = get_leftop(oldrinfo->clause); Node *oldright = get_rightop(oldrinfo->clause); Node *newguy = NULL; /* must advance olditem before list_delete possibly pfree's it */ olditem = lnext(olditem); if (list_member(equalexprs, oldleft)) newguy = oldright; else if (list_member(equalexprs, oldright)) newguy = oldleft; else continue; if (equal(newguy, newright)) return true; /* we proved new clause is redundant */ equalexprs = lcons(newguy, equalexprs); someadded = true; /* * Remove this qual from list, since we don't need it anymore. */ oldquals = list_delete_ptr(oldquals, oldrinfo); } } while (someadded); return false; /* it's not redundant */}/***************************************************************************** * * CHECKS FOR MERGEJOINABLE AND HASHJOINABLE CLAUSES * *****************************************************************************//* * check_mergejoinable * If the restrictinfo's clause is mergejoinable, set the mergejoin * info fields in the restrictinfo. * * Currently, we support mergejoin for binary opclauses where * the operator is a mergejoinable operator. The arguments can be * anything --- as long as there are no volatile functions in them. */static voidcheck_mergejoinable(RestrictInfo *restrictinfo){ Expr *clause = restrictinfo->clause; Oid opno, leftOp, rightOp; if (!is_opclause(clause)) return; if (list_length(((OpExpr *) clause)->args) != 2) return; opno = ((OpExpr *) clause)->opno; if (op_mergejoinable(opno, &leftOp, &rightOp) && !contain_volatile_functions((Node *) clause)) { restrictinfo->mergejoinoperator = opno; restrictinfo->left_sortop = leftOp; restrictinfo->right_sortop = rightOp; }}/* * check_hashjoinable * If the restrictinfo's clause is hashjoinable, set the hashjoin * info fields in the restrictinfo. * * Currently, we support hashjoin for binary opclauses where * the operator is a hashjoinable operator. The arguments can be * anything --- as long as there are no volatile functions in them. */static voidcheck_hashjoinable(RestrictInfo *restrictinfo){ Expr *clause = restrictinfo->clause; Oid opno; if (!is_opclause(clause)) return; if (list_length(((OpExpr *) clause)->args) != 2) return; opno = ((OpExpr *) clause)->opno; if (op_hashjoinable(opno) && !contain_volatile_functions((Node *) clause)) restrictinfo->hashjoinoperator = opno;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -