diff options
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 756 |
1 files changed, 312 insertions, 444 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 3dc7229cf35..b49df212709 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.127 2007/01/08 16:47:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.128 2007/01/20 20:45:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,8 +37,6 @@ int from_collapse_limit; int join_collapse_limit; -static void add_vars_to_targetlist(PlannerInfo *root, List *vars, - Relids where_needed); static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, Relids *qualscope); static OuterJoinInfo *make_outerjoininfo(PlannerInfo *root, @@ -51,8 +49,7 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause, Relids qualscope, Relids ojscope, Relids outerjoin_nonnullable); -static bool qual_is_redundant(PlannerInfo *root, RestrictInfo *restrictinfo, - List *restrictlist); +static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p); static void check_mergejoinable(RestrictInfo *restrictinfo); static void check_hashjoinable(RestrictInfo *restrictinfo); @@ -144,7 +141,7 @@ build_base_rel_tlists(PlannerInfo *root, List *final_tlist) * as being needed for the indicated join (or for final output if * where_needed includes "relation 0"). */ -static void +void add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed) { ListCell *temp; @@ -590,17 +587,17 @@ make_outerjoininfo(PlannerInfo *root, * Add clause information to either the baserestrictinfo or joininfo list * (depending on whether the clause is a join) of each base relation * mentioned in the clause. A RestrictInfo node is created and added to - * the appropriate list for each rel. Also, if the clause uses a + * the appropriate list for each rel. Alternatively, if the clause uses a * mergejoinable operator and is not delayed by outer-join rules, enter - * the left- and right-side expressions into the query's lists of - * equijoined vars. + * the left- and right-side expressions into the query's list of + * EquivalenceClasses. * * 'clause': the qual clause to be distributed * 'is_pushed_down': if TRUE, force the clause to be marked 'is_pushed_down' * (this indicates the clause came from a FromExpr, not a JoinExpr) * 'is_deduced': TRUE if the qual came from implied-equality deduction * 'below_outer_join': TRUE if the qual is from a JOIN/ON that is below the - * nullable side of a higher-level outer join. + * nullable side of a higher-level outer join * 'qualscope': set of baserels the qual's syntactic scope covers * 'ojscope': NULL if not an outer-join qual, else the minimum set of baserels * needed to form this join @@ -625,11 +622,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, Relids relids; bool outerjoin_delayed; bool pseudoconstant = false; - bool maybe_equijoin; + bool maybe_equivalence; bool maybe_outer_join; RestrictInfo *restrictinfo; - RelOptInfo *rel; - List *vars; /* * Retrieve all relids mentioned within the clause. @@ -705,108 +700,57 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, if (is_deduced) { /* - * If the qual came from implied-equality deduction, we always - * evaluate the qual at its natural semantic level. It is the - * responsibility of the deducer not to create any quals that should - * be delayed by outer-join rules. + * If the qual came from implied-equality deduction, it should + * not be outerjoin-delayed, else deducer blew it. But we can't + * check this because the ojinfo list may now contain OJs above + * where the qual belongs. */ - Assert(bms_equal(relids, qualscope)); Assert(!ojscope); - Assert(!pseudoconstant); - /* Needn't feed it back for more deductions */ outerjoin_delayed = false; - maybe_equijoin = false; + /* Don't feed it back for more deductions */ + maybe_equivalence = false; maybe_outer_join = false; } else if (bms_overlap(relids, outerjoin_nonnullable)) { /* * The qual is attached to an outer join and mentions (some of the) - * rels on the nonnullable side. Force the qual to be evaluated - * exactly at the level of joining corresponding to the outer join. We - * cannot let it get pushed down into the nonnullable side, since then - * we'd produce no output rows, rather than the intended single - * null-extended row, for any nonnullable-side rows failing the qual. + * rels on the nonnullable side. * * Note: an outer-join qual that mentions only nullable-side rels can * be pushed down into the nullable side without changing the join - * result, so we treat it the same as an ordinary inner-join qual, - * except for not setting maybe_equijoin (see below). + * result, so we treat it almost the same as an ordinary inner-join + * qual (see below). + * + * We can't use such a clause to deduce equivalence (the left and right + * sides might be unequal above the join because one of them has gone + * to NULL) ... but we might be able to use it for more limited + * deductions, if there are no lower outer joins that delay its + * application. If so, consider adding it to the lists of set-aside + * clauses. + */ + maybe_equivalence = false; + maybe_outer_join = !check_outerjoin_delay(root, &relids); + + /* + * Now force the qual to be evaluated exactly at the level of joining + * corresponding to the outer join. We cannot let it get pushed down + * into the nonnullable side, since then we'd produce no output rows, + * rather than the intended single null-extended row, for any + * nonnullable-side rows failing the qual. + * + * (Do this step after calling check_outerjoin_delay, because that + * trashes relids.) */ Assert(ojscope); relids = ojscope; outerjoin_delayed = true; Assert(!pseudoconstant); - - /* - * We can't use such a clause to deduce equijoin (the left and right - * sides might be unequal above the join because one of them has gone - * to NULL) ... but we might be able to use it for more limited - * purposes. Note: for the current uses of deductions from an - * outer-join clause, it seems safe to make the deductions even when - * the clause is below a higher-level outer join; so we do not check - * below_outer_join here. - */ - maybe_equijoin = false; - maybe_outer_join = true; } else { - /* - * For a non-outer-join qual, we can evaluate the qual as soon as (1) - * we have all the rels it mentions, and (2) we are at or above any - * outer joins that can null any of these rels and are below the - * syntactic location of the given qual. We must enforce (2) because - * pushing down such a clause below the OJ might cause the OJ to emit - * null-extended rows that should not have been formed, or that should - * have been rejected by the clause. (This is only an issue for - * non-strict quals, since if we can prove a qual mentioning only - * nullable rels is strict, we'd have reduced the outer join to an - * inner join in reduce_outer_joins().) - * - * To enforce (2), scan the oj_info_list and merge the required-relid - * sets of any such OJs into the clause's own reference list. At the - * time we are called, the oj_info_list contains only outer joins - * below this qual. We have to repeat the scan until no new relids - * get added; this ensures that the qual is suitably delayed regardless - * of the order in which OJs get executed. As an example, if we have - * one OJ with LHS=A, RHS=B, and one with LHS=B, RHS=C, it is implied - * that these can be done in either order; if the B/C join is done - * first then the join to A can null C, so a qual actually mentioning - * only C cannot be applied below the join to A. - */ - bool found_some; - - outerjoin_delayed = false; - do { - ListCell *l; - - found_some = false; - foreach(l, root->oj_info_list) - { - OuterJoinInfo *ojinfo = (OuterJoinInfo *) lfirst(l); - - /* do we have any nullable rels of this OJ? */ - if (bms_overlap(relids, ojinfo->min_righthand) || - (ojinfo->is_full_join && - bms_overlap(relids, ojinfo->min_lefthand))) - { - /* yes; do we have all its rels? */ - if (!bms_is_subset(ojinfo->min_lefthand, relids) || - !bms_is_subset(ojinfo->min_righthand, relids)) - { - /* no, so add them in */ - relids = bms_add_members(relids, - ojinfo->min_lefthand); - relids = bms_add_members(relids, - ojinfo->min_righthand); - outerjoin_delayed = true; - /* we'll need another iteration */ - found_some = true; - } - } - } - } while (found_some); + /* Normal qual clause; check to see if must be delayed by outer join */ + outerjoin_delayed = check_outerjoin_delay(root, &relids); if (outerjoin_delayed) { @@ -816,26 +760,27 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, * Because application of the qual will be delayed by outer join, * we mustn't assume its vars are equal everywhere. */ - maybe_equijoin = false; + maybe_equivalence = false; } else { /* - * Qual is not delayed by any lower outer-join restriction. If it - * is not itself below or within an outer join, we can consider it - * "valid everywhere", so consider feeding it to the equijoin - * machinery. (If it is within an outer join, we can't consider - * it "valid everywhere": once the contained variables have gone - * to NULL, we'd be asserting things like NULL = NULL, which is - * not true.) + * Qual is not delayed by any lower outer-join restriction, so + * we can consider feeding it to the equivalence machinery. + * However, if it's itself within an outer-join clause, treat it + * as though it appeared below that outer join (note that we can + * only get here when the clause references only nullable-side + * rels). */ - if (!below_outer_join && outerjoin_nonnullable == NULL) - maybe_equijoin = true; - else - maybe_equijoin = false; + maybe_equivalence = true; + if (outerjoin_nonnullable != NULL) + below_outer_join = true; } - /* Since it doesn't mention the LHS, it's certainly not an OJ clause */ + /* + * Since it doesn't mention the LHS, it's certainly not useful as a + * set-aside OJ clause, even if it's in an OJ. + */ maybe_outer_join = false; } @@ -860,118 +805,65 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, relids); /* - * Figure out where to attach it. + * If it's a join clause (either naturally, or because delayed by + * outer-join rules), add vars used in the clause to targetlists of + * their relations, so that they will be emitted by the plan nodes that + * scan those relations (else they won't be available at the join node!). + * + * Note: if the clause gets absorbed into an EquivalenceClass then this + * may be unnecessary, but for now we have to do it to cover the case + * where the EC becomes ec_broken and we end up reinserting the original + * clauses into the plan. */ - switch (bms_membership(relids)) + if (bms_membership(relids) == BMS_MULTIPLE) { - case BMS_SINGLETON: - - /* - * There is only one relation participating in 'clause', so - * 'clause' is a restriction clause for that relation. - */ - rel = find_base_rel(root, bms_singleton_member(relids)); - - /* - * Check for a "mergejoinable" clause even though it's not a join - * clause. This is so that we can recognize that "a.x = a.y" - * makes x and y eligible to be considered equal, even when they - * belong to the same rel. Without this, we would not recognize - * that "a.x = a.y AND a.x = b.z AND a.y = c.q" allows us to - * consider z and q equal after their rels are joined. - */ - check_mergejoinable(restrictinfo); + List *vars = pull_var_clause(clause, false); - /* - * If the clause was deduced from implied equality, check to see - * whether it is redundant with restriction clauses we already - * have for this rel. Note we cannot apply this check to - * user-written clauses, since we haven't found the canonical - * pathkey sets yet while processing user clauses. (NB: no - * comparable check is done in the join-clause case; redundancy - * will be detected when the join clause is moved into a join - * rel's restriction list.) - */ - if (!is_deduced || - !qual_is_redundant(root, restrictinfo, - rel->baserestrictinfo)) - { - /* Add clause to rel's restriction list */ - rel->baserestrictinfo = lappend(rel->baserestrictinfo, - restrictinfo); - } - break; - case BMS_MULTIPLE: - - /* - * 'clause' is a join clause, since there is more than one rel in - * the relid set. - */ - - /* - * Check for hash or mergejoinable operators. - * - * We don't bother setting the hashjoin info if we're not going to - * need it. We do want to know about mergejoinable ops in all - * cases, however, because we use mergejoinable ops for other - * purposes such as detecting redundant clauses. - */ - check_mergejoinable(restrictinfo); - if (enable_hashjoin) - check_hashjoinable(restrictinfo); - - /* - * Add clause to the join lists of all the relevant relations. - */ - add_join_clause_to_rels(root, restrictinfo, relids); - - /* - * Add vars used in the join clause to targetlists of their - * relations, so that they will be emitted by the plan nodes that - * scan those relations (else they won't be available at the join - * node!). - */ - vars = pull_var_clause(clause, false); - add_vars_to_targetlist(root, vars, relids); - list_free(vars); - break; - default: - - /* - * 'clause' references no rels, and therefore we have no place to - * attach it. Shouldn't get here if callers are working properly. - */ - elog(ERROR, "cannot cope with variable-free clause"); - break; + add_vars_to_targetlist(root, vars, relids); + list_free(vars); } /* - * If the clause has a mergejoinable operator, we may be able to deduce - * more things from it under the principle of transitivity. + * We check "mergejoinability" of every clause, not only join clauses, + * because we want to know about equivalences between vars of the same + * relation, or between vars and consts. + */ + check_mergejoinable(restrictinfo); + + /* + * If it is a true equivalence clause, send it to the EquivalenceClass + * machinery. We do *not* attach it directly to any restriction or join + * lists. The EC code will propagate it to the appropriate places later. * - * If it is not an outer-join qualification nor bubbled up due to an outer - * join, then the two sides represent equivalent PathKeyItems for path - * keys: any path that is sorted by one side will also be sorted by the - * other (as soon as the two rels are joined, that is). Pass such clauses - * to add_equijoined_keys. + * If the clause has a mergejoinable operator and is not outerjoin-delayed, + * yet isn't an equivalence because it is an outer-join clause, the EC + * code may yet be able to do something with it. We add it to appropriate + * lists for further consideration later. Specifically: * - * If it is a left or right outer-join qualification that relates the two - * sides of the outer join (no funny business like leftvar1 = leftvar2 + - * rightvar), we add it to root->left_join_clauses or + * If it is a left or right outer-join qualification that relates the + * two sides of the outer join (no funny business like leftvar1 = + * leftvar2 + rightvar), we add it to root->left_join_clauses or * root->right_join_clauses according to which side the nonnullable * variable appears on. * * If it is a full outer-join qualification, we add it to * root->full_join_clauses. (Ideally we'd discard cases that aren't * leftvar = rightvar, as we do for left/right joins, but this routine - * doesn't have the info needed to do that; and the current usage of the - * full_join_clauses list doesn't require that, so it's not currently - * worth complicating this routine's API to make it possible.) + * doesn't have the info needed to do that; and the current usage of + * the full_join_clauses list doesn't require that, so it's not + * currently worth complicating this routine's API to make it possible.) + * + * If none of the above hold, pass it off to + * distribute_restrictinfo_to_rels(). */ - if (restrictinfo->mergejoinoperator != InvalidOid) + if (restrictinfo->mergeopfamilies) { - if (maybe_equijoin) - add_equijoined_keys(root, restrictinfo); + if (maybe_equivalence) + { + if (process_equivalence(root, restrictinfo, below_outer_join)) + return; + /* EC rejected it, so pass to distribute_restrictinfo_to_rels */ + } else if (maybe_outer_join && restrictinfo->can_join) { if (bms_is_subset(restrictinfo->left_relids, @@ -982,8 +874,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, /* we have outervar = innervar */ root->left_join_clauses = lappend(root->left_join_clauses, restrictinfo); + return; } - else if (bms_is_subset(restrictinfo->right_relids, + if (bms_is_subset(restrictinfo->right_relids, outerjoin_nonnullable) && !bms_overlap(restrictinfo->left_relids, outerjoin_nonnullable)) @@ -991,166 +884,213 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, /* we have innervar = outervar */ root->right_join_clauses = lappend(root->right_join_clauses, restrictinfo); + return; } - else if (bms_equal(outerjoin_nonnullable, qualscope)) + 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); + return; } } } + + /* No EC special case applies, so push it into the clause lists */ + distribute_restrictinfo_to_rels(root, 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. + * check_outerjoin_delay + * Detect whether a qual referencing the given relids must be delayed + * in application due to the presence of a lower outer join. * - * 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. + * If so, add relids to *relids_p to reflect the lowest safe level for + * evaluating the qual, and return TRUE. + * + * For a non-outer-join qual, we can evaluate the qual as soon as (1) we have + * all the rels it mentions, and (2) we are at or above any outer joins that + * can null any of these rels and are below the syntactic location of the + * given qual. We must enforce (2) because pushing down such a clause below + * the OJ might cause the OJ to emit null-extended rows that should not have + * been formed, or that should have been rejected by the clause. (This is + * only an issue for non-strict quals, since if we can prove a qual mentioning + * only nullable rels is strict, we'd have reduced the outer join to an inner + * join in reduce_outer_joins().) + * + * To enforce (2), scan the oj_info_list and merge the required-relid sets of + * any such OJs into the clause's own reference list. At the time we are + * called, the oj_info_list contains only outer joins below this qual. We + * have to repeat the scan until no new relids get added; this ensures that + * the qual is suitably delayed regardless of the order in which OJs get + * executed. As an example, if we have one OJ with LHS=A, RHS=B, and one with + * LHS=B, RHS=C, it is implied that these can be done in either order; if the + * B/C join is done first then the join to A can null C, so a qual actually + * mentioning only C cannot be applied below the join to A. + * + * For an outer-join qual, this isn't going to determine where we place the + * qual, but we need to determine outerjoin_delayed anyway so we can decide + * whether the qual is potentially useful for equivalence deductions. */ -void -process_implied_equality(PlannerInfo *root, - Node *item1, Node *item2, - Oid sortop1, Oid sortop2, - Relids item1_relids, Relids item2_relids, - bool delete_it) +static bool +check_outerjoin_delay(PlannerInfo *root, Relids *relids_p) { - 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); + Relids relids = *relids_p; + bool outerjoin_delayed; + bool found_some; - rel1 = find_base_rel(root, first_rel); - restrictlist = rel1->joininfo; - } + outerjoin_delayed = false; + do { + ListCell *l; - /* - * 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_some = false; + foreach(l, root->oj_info_list) { - /* found a matching clause */ - if (delete_it) + OuterJoinInfo *ojinfo = (OuterJoinInfo *) lfirst(l); + + /* do we reference any nullable rels of this OJ? */ + if (bms_overlap(relids, ojinfo->min_righthand) || + (ojinfo->is_full_join && + bms_overlap(relids, ojinfo->min_lefthand))) { - if (membership == BMS_SINGLETON) - { - /* delete it from local restrictinfo list */ - rel1->baserestrictinfo = list_delete_ptr(rel1->baserestrictinfo, - restrictinfo); - } - else + /* yes; have we included all its rels in relids? */ + if (!bms_is_subset(ojinfo->min_lefthand, relids) || + !bms_is_subset(ojinfo->min_righthand, relids)) { - /* let joininfo.c do it */ - remove_join_clause_from_rels(root, restrictinfo, relids); + /* no, so add them in */ + relids = bms_add_members(relids, ojinfo->min_lefthand); + relids = bms_add_members(relids, ojinfo->min_righthand); + outerjoin_delayed = true; + /* we'll need another iteration */ + found_some = true; } } - return; /* done */ } - } + } while (found_some); - /* Didn't find it. Done if deletion requested */ - if (delete_it) - return; + *relids_p = relids; + return outerjoin_delayed; +} - /* - * 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(NULL, list_make1(makeString("=")), - ltype, rtype, - true, -1); - if (!HeapTupleIsValid(eq_operator)) +/* + * distribute_restrictinfo_to_rels + * Push a completed RestrictInfo into the proper restriction or join + * clause list(s). + * + * This is the last step of distribute_qual_to_rels() for ordinary qual + * clauses. Clauses that are interesting for equivalence-class processing + * are diverted to the EC machinery, but may ultimately get fed back here. + */ +void +distribute_restrictinfo_to_rels(PlannerInfo *root, + RestrictInfo *restrictinfo) +{ + Relids relids = restrictinfo->required_relids; + RelOptInfo *rel; + + switch (bms_membership(relids)) { - /* - * 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)))); + case BMS_SINGLETON: + + /* + * There is only one relation participating in the clause, so + * it is a restriction clause for that relation. + */ + rel = find_base_rel(root, bms_singleton_member(relids)); + + /* Add clause to rel's restriction list */ + rel->baserestrictinfo = lappend(rel->baserestrictinfo, + restrictinfo); + break; + case BMS_MULTIPLE: + + /* + * The clause is a join clause, since there is more than one rel + * in its relid set. + */ + + /* + * Check for hashjoinable operators. (We don't bother setting + * the hashjoin info if we're not going to need it.) + */ + if (enable_hashjoin) + check_hashjoinable(restrictinfo); + + /* + * Add clause to the join lists of all the relevant relations. + */ + add_join_clause_to_rels(root, restrictinfo, relids); + break; + default: + + /* + * clause references no rels, and therefore we have no place to + * attach it. Shouldn't get here if callers are working properly. + */ + elog(ERROR, "cannot cope with variable-free clause"); + break; } - pgopform = (Form_pg_operator) GETSTRUCT(eq_operator); +} - /* - * Let's just make sure this appears to be a compatible operator. - * - * XXX needs work - */ - if (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)))); +/* + * process_implied_equality + * Create a restrictinfo item that says "item1 op item2", and push it + * into the appropriate lists. (In practice opno is always a btree + * equality operator.) + * + * "qualscope" is the nominal syntactic level to impute to the restrictinfo. + * This must contain at least all the rels used in the expressions, but it + * is used only to set the qual application level when both exprs are + * variable-free. Otherwise the qual is applied at the lowest join level + * that provides all its variables. + * + * "both_const" indicates whether both items are known pseudo-constant; + * in this case it is worth applying eval_const_expressions() in case we + * can produce constant TRUE or constant FALSE. (Otherwise it's not, + * because the expressions went through eval_const_expressions already.) + * + * This is currently used only when an EquivalenceClass is found to + * contain pseudoconstants. See path/pathkeys.c for more details. + */ +void +process_implied_equality(PlannerInfo *root, + Oid opno, + Expr *item1, + Expr *item2, + Relids qualscope, + bool below_outer_join, + bool both_const) +{ + Expr *clause; /* - * 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...) + * 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 */ + clause = make_opclause(opno, BOOLOID, /* opresulttype */ false, /* opretset */ (Expr *) copyObject(item1), (Expr *) copyObject(item2)); - ReleaseSysCache(eq_operator); + /* If both constant, try to reduce to a boolean constant. */ + if (both_const) + { + clause = (Expr *) eval_const_expressions((Node *) clause); + + /* If we produced const TRUE, just drop the clause */ + if (clause && IsA(clause, Const)) + { + Const *cclause = (Const *) clause; + + Assert(cclause->consttype == BOOLOID); + if (!cclause->constisnull && DatumGetBool(cclause->constvalue)) + return; + } + } + + /* Make a copy of qualscope to avoid problems if source EC changes */ + qualscope = bms_copy(qualscope); /* * Push the new clause into all the appropriate restrictinfo lists. @@ -1159,119 +1099,53 @@ process_implied_equality(PlannerInfo *root, * taken for an original JOIN/ON clause. */ distribute_qual_to_rels(root, (Node *) clause, - true, true, false, relids, NULL, NULL); + true, true, below_outer_join, + qualscope, NULL, NULL); } /* - * 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. + * build_implied_join_equality --- build a RestrictInfo for a derived equality * - * 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. + * This overlaps the functionality of process_implied_equality(), but we + * must return the RestrictInfo, not push it into the joininfo tree. */ -static bool -qual_is_redundant(PlannerInfo *root, - RestrictInfo *restrictinfo, - List *restrictlist) +RestrictInfo * +build_implied_join_equality(Oid opno, + Expr *item1, + Expr *item2, + Relids qualscope) { - 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; + RestrictInfo *restrictinfo; + Expr *clause; /* - * 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. + * Build the new clause. Copy to ensure it shares no substructure with + * original (this is necessary in case there are subselects in there...) */ - oldquals = NIL; - foreach(olditem, restrictlist) - { - RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem); + clause = make_opclause(opno, + BOOLOID, /* opresulttype */ + false, /* opretset */ + (Expr *) copyObject(item1), + (Expr *) copyObject(item2)); - 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; + /* Make a copy of qualscope to avoid problems if source EC changes */ + qualscope = bms_copy(qualscope); /* - * 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. + * Build the RestrictInfo node itself. */ - 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 */ + restrictinfo = make_restrictinfo(clause, + true, /* is_pushed_down */ + false, /* outerjoin_delayed */ + false, /* pseudoconstant */ + qualscope); + + /* Set mergejoinability info always, and hashjoinability if enabled */ + check_mergejoinable(restrictinfo); + if (enable_hashjoin) + check_hashjoinable(restrictinfo); + + return restrictinfo; } @@ -1294,10 +1168,7 @@ static void check_mergejoinable(RestrictInfo *restrictinfo) { Expr *clause = restrictinfo->clause; - Oid opno, - leftOp, - rightOp; - Oid opfamily; + Oid opno; if (restrictinfo->pseudoconstant) return; @@ -1310,16 +1181,13 @@ check_mergejoinable(RestrictInfo *restrictinfo) if (op_mergejoinable(opno) && !contain_volatile_functions((Node *) clause)) - { - /* XXX for the moment, continue to force use of particular sortops */ - if (get_op_mergejoin_info(opno, &leftOp, &rightOp, &opfamily)) - { - restrictinfo->mergejoinoperator = opno; - restrictinfo->left_sortop = leftOp; - restrictinfo->right_sortop = rightOp; - restrictinfo->mergeopfamily = opfamily; - } - } + restrictinfo->mergeopfamilies = get_mergejoin_opfamilies(opno); + + /* + * Note: op_mergejoinable is just a hint; if we fail to find the + * operator in any btree opfamilies, mergeopfamilies remains NIL + * and so the clause is not treated as mergejoinable. + */ } /* |