aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/equivclass.c26
-rw-r--r--src/backend/optimizer/util/relnode.c103
2 files changed, 111 insertions, 18 deletions
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 51d806326eb..d871396e20c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -2443,15 +2443,17 @@ find_join_domain(PlannerInfo *root, Relids relids)
* Detect whether two expressions are known equal due to equivalence
* relationships.
*
- * Actually, this only shows that the expressions are equal according
- * to some opfamily's notion of equality --- but we only use it for
- * selectivity estimation, so a fuzzy idea of equality is OK.
+ * If opfamily is given, the expressions must be known equal per the semantics
+ * of that opfamily (note it has to be a btree opfamily, since those are the
+ * only opfamilies equivclass.c deals with). If opfamily is InvalidOid, we'll
+ * return true if they're equal according to any opfamily, which is fuzzy but
+ * OK for estimation purposes.
*
* Note: does not bother to check for "equal(item1, item2)"; caller must
* check that case if it's possible to pass identical items.
*/
bool
-exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
+exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
{
ListCell *lc1;
@@ -2466,6 +2468,17 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
if (ec->ec_has_volatile)
continue;
+ /*
+ * It's okay to consider ec_broken ECs here. Brokenness just means we
+ * couldn't derive all the implied clauses we'd have liked to; it does
+ * not invalidate our knowledge that the members are equal.
+ */
+
+ /* Ignore if this EC doesn't use specified opfamily */
+ if (OidIsValid(opfamily) &&
+ !list_member_oid(ec->ec_opfamilies, opfamily))
+ continue;
+
foreach(lc2, ec->ec_members)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
@@ -2494,8 +2507,7 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
* (In principle there might be more than one matching eclass if multiple
* collations are involved, but since collation doesn't matter for equality,
* we ignore that fine point here.) This is much like exprs_known_equal,
- * except that we insist on the comparison operator matching the eclass, so
- * that the result is definite not approximate.
+ * except for the format of the input.
*
* On success, we also set fkinfo->eclass[colno] to the matching eclass,
* and set fkinfo->fk_eclass_member[colno] to the eclass member for the
@@ -2536,7 +2548,7 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
/* Never match to a volatile EC */
if (ec->ec_has_volatile)
continue;
- /* Note: it seems okay to match to "broken" eclasses here */
+ /* It's okay to consider "broken" ECs here, see exprs_known_equal */
foreach(lc2, ec->ec_members)
{
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 971d1c7aae5..d7266e4cdba 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -2080,10 +2080,9 @@ have_partkey_equi_join(PlannerInfo *root, RelOptInfo *joinrel,
JoinType jointype, List *restrictlist)
{
PartitionScheme part_scheme = rel1->part_scheme;
+ bool pk_known_equal[PARTITION_MAX_KEYS];
+ int num_equal_pks;
ListCell *lc;
- int cnt_pks;
- bool pk_has_clause[PARTITION_MAX_KEYS];
- bool strict_op;
/*
* This function must only be called when the joined relations have same
@@ -2092,13 +2091,19 @@ have_partkey_equi_join(PlannerInfo *root, RelOptInfo *joinrel,
Assert(rel1->part_scheme == rel2->part_scheme);
Assert(part_scheme);
- memset(pk_has_clause, 0, sizeof(pk_has_clause));
+ /* We use a bool array to track which partkey columns are known equal */
+ memset(pk_known_equal, 0, sizeof(pk_known_equal));
+ /* ... as well as a count of how many are known equal */
+ num_equal_pks = 0;
+
+ /* First, look through the join's restriction clauses */
foreach(lc, restrictlist)
{
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
OpExpr *opexpr;
Expr *expr1;
Expr *expr2;
+ bool strict_op;
int ipk1;
int ipk2;
@@ -2176,11 +2181,15 @@ have_partkey_equi_join(PlannerInfo *root, RelOptInfo *joinrel,
if (ipk1 != ipk2)
continue;
+ /* Ignore clause if we already proved these keys equal. */
+ if (pk_known_equal[ipk1])
+ continue;
+
/*
* The clause allows partitionwise join only if it uses the same
* operator family as that specified by the partition key.
*/
- if (rel1->part_scheme->strategy == PARTITION_STRATEGY_HASH)
+ if (part_scheme->strategy == PARTITION_STRATEGY_HASH)
{
if (!OidIsValid(rinfo->hashjoinoperator) ||
!op_in_opfamily(rinfo->hashjoinoperator,
@@ -2192,17 +2201,89 @@ have_partkey_equi_join(PlannerInfo *root, RelOptInfo *joinrel,
continue;
/* Mark the partition key as having an equi-join clause. */
- pk_has_clause[ipk1] = true;
+ pk_known_equal[ipk1] = true;
+
+ /* We can stop examining clauses once we prove all keys equal. */
+ if (++num_equal_pks == part_scheme->partnatts)
+ return true;
}
- /* Check whether every partition key has an equi-join condition. */
- for (cnt_pks = 0; cnt_pks < part_scheme->partnatts; cnt_pks++)
+ /*
+ * Also check to see if any keys are known equal by equivclass.c. In most
+ * cases there would have been a join restriction clause generated from
+ * any EC that had such knowledge, but there might be no such clause, or
+ * it might happen to constrain other members of the ECs than the ones we
+ * are looking for.
+ */
+ for (int ipk = 0; ipk < part_scheme->partnatts; ipk++)
{
- if (!pk_has_clause[cnt_pks])
- return false;
+ Oid btree_opfamily;
+
+ /* Ignore if we already proved these keys equal. */
+ if (pk_known_equal[ipk])
+ continue;
+
+ /*
+ * We need a btree opfamily to ask equivclass.c about. If the
+ * partopfamily is a hash opfamily, look up its equality operator, and
+ * select some btree opfamily that that operator is part of. (Any
+ * such opfamily should be good enough, since equivclass.c will track
+ * multiple opfamilies as appropriate.)
+ */
+ if (part_scheme->strategy == PARTITION_STRATEGY_HASH)
+ {
+ Oid eq_op;
+ List *eq_opfamilies;
+
+ eq_op = get_opfamily_member(part_scheme->partopfamily[ipk],
+ part_scheme->partopcintype[ipk],
+ part_scheme->partopcintype[ipk],
+ HTEqualStrategyNumber);
+ if (!OidIsValid(eq_op))
+ break; /* we're not going to succeed */
+ eq_opfamilies = get_mergejoin_opfamilies(eq_op);
+ if (eq_opfamilies == NIL)
+ break; /* we're not going to succeed */
+ btree_opfamily = linitial_oid(eq_opfamilies);
+ }
+ else
+ btree_opfamily = part_scheme->partopfamily[ipk];
+
+ /*
+ * We consider only non-nullable partition keys here; nullable ones
+ * would not be treated as part of the same equivalence classes as
+ * non-nullable ones.
+ */
+ foreach(lc, rel1->partexprs[ipk])
+ {
+ Node *expr1 = (Node *) lfirst(lc);
+ ListCell *lc2;
+
+ foreach(lc2, rel2->partexprs[ipk])
+ {
+ Node *expr2 = (Node *) lfirst(lc2);
+
+ if (exprs_known_equal(root, expr1, expr2, btree_opfamily))
+ {
+ pk_known_equal[ipk] = true;
+ break;
+ }
+ }
+ if (pk_known_equal[ipk])
+ break;
+ }
+
+ if (pk_known_equal[ipk])
+ {
+ /* We can stop examining keys once we prove all keys equal. */
+ if (++num_equal_pks == part_scheme->partnatts)
+ return true;
+ }
+ else
+ break; /* no chance to succeed, give up */
}
- return true;
+ return false;
}
/*