summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/initsplan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r--src/backend/optimizer/plan/initsplan.c756
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.
+ */
}
/*