aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/path')
-rw-r--r--src/backend/optimizer/path/equivclass.c44
-rw-r--r--src/backend/optimizer/path/indxpath.c18
-rw-r--r--src/backend/optimizer/path/pathkeys.c17
3 files changed, 65 insertions, 14 deletions
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index b653d6cb35c..c115c2148db 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -491,6 +491,15 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
* sortref is the SortGroupRef of the originating SortGroupClause, if any,
* or zero if not. (It should never be zero if the expression is volatile!)
*
+ * If rel is not NULL, it identifies a specific relation we're considering
+ * a path for, and indicates that child EC members for that relation can be
+ * considered. Otherwise child members are ignored. (Note: since child EC
+ * members aren't guaranteed unique, a non-NULL value means that there could
+ * be more than one EC that matches the expression; if so it's order-dependent
+ * which one you get. This is annoying but it only happens in corner cases,
+ * so for now we live with just reporting the first match. See also
+ * generate_implied_equalities_for_indexcol and match_pathkeys_to_index.)
+ *
* If create_it is TRUE, we'll build a new EquivalenceClass when there is no
* match. If create_it is FALSE, we just return NULL when no match.
*
@@ -511,6 +520,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
Oid opcintype,
Oid collation,
Index sortref,
+ Relids rel,
bool create_it)
{
EquivalenceClass *newec;
@@ -549,6 +559,13 @@ get_eclass_for_sort_expr(PlannerInfo *root,
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
/*
+ * Ignore child members unless they match the request.
+ */
+ if (cur_em->em_is_child &&
+ !bms_equal(cur_em->em_relids, rel))
+ continue;
+
+ /*
* If below an outer join, don't match constants: they're not as
* constant as they look.
*/
@@ -1505,6 +1522,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+ Assert(!cur_em->em_is_child); /* no children yet */
if (equal(outervar, cur_em->em_expr))
{
match = true;
@@ -1626,6 +1644,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
foreach(lc2, cur_ec->ec_members)
{
coal_em = (EquivalenceMember *) lfirst(lc2);
+ Assert(!coal_em->em_is_child); /* no children yet */
if (IsA(coal_em->em_expr, CoalesceExpr))
{
CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -1747,6 +1766,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+ if (em->em_is_child)
+ continue; /* ignore children here */
if (equal(item1, em->em_expr))
item1member = true;
else if (equal(item2, em->em_expr))
@@ -1800,6 +1821,9 @@ add_child_rel_equivalences(PlannerInfo *root,
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+ if (cur_em->em_is_child)
+ continue; /* ignore children here */
+
/* Does it reference (only) parent_rel? */
if (bms_equal(cur_em->em_relids, parent_rel->relids))
{
@@ -1908,7 +1932,16 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
!bms_is_subset(rel->relids, cur_ec->ec_relids))
continue;
- /* Scan members, looking for a match to the indexable column */
+ /*
+ * Scan members, looking for a match to the indexable column. Note
+ * that child EC members are considered, but only when they belong to
+ * the target relation. (Unlike regular members, the same expression
+ * could be a child member of more than one EC. Therefore, it's
+ * potentially order-dependent which EC a child relation's index
+ * column gets matched to. This is annoying but it only happens in
+ * corner cases, so for now we live with just reporting the first
+ * match. See also get_eclass_for_sort_expr.)
+ */
cur_em = NULL;
foreach(lc2, cur_ec->ec_members)
{
@@ -1933,6 +1966,9 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
Oid eq_op;
RestrictInfo *rinfo;
+ if (other_em->em_is_child)
+ continue; /* ignore children here */
+
/* Make sure it'll be a join to a different rel */
if (other_em == cur_em ||
bms_overlap(other_em->em_relids, rel->relids))
@@ -2187,8 +2223,10 @@ eclass_useful_for_merging(EquivalenceClass *eclass,
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
- if (!cur_em->em_is_child &&
- !bms_overlap(cur_em->em_relids, rel->relids))
+ if (cur_em->em_is_child)
+ continue; /* ignore children here */
+
+ if (!bms_overlap(cur_em->em_relids, rel->relids))
return true;
}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 2f088b79787..89b42da6b46 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -2157,7 +2157,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
if (pathkey->pk_eclass->ec_has_volatile)
return;
- /* Try to match eclass member expression(s) to index */
+ /*
+ * Try to match eclass member expression(s) to index. Note that child
+ * EC members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could
+ * be considered to match more than one pathkey list, which is OK
+ * here. See also get_eclass_for_sort_expr.)
+ */
foreach(lc2, pathkey->pk_eclass->ec_members)
{
EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
@@ -2581,15 +2588,6 @@ match_index_to_operand(Node *operand,
int indkey;
/*
- * Ignore any PlaceHolderVar nodes above the operand. This is needed so
- * that we can successfully use expression-index constraints pushed down
- * through appendrels (UNION ALL). It's safe because a PlaceHolderVar
- * appearing in a relation-scan-level expression is certainly a no-op.
- */
- while (operand && IsA(operand, PlaceHolderVar))
- operand = (Node *) ((PlaceHolderVar *) operand)->phexpr;
-
- /*
* Ignore any RelabelType node above the operand. This is needed to be
* able to apply indexscanning in binary-compatible-operator cases. Note:
* we can assume there is at most one RelabelType node;
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 653387e582d..4ff8016666d 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -221,6 +221,11 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
* If the PathKey is being generated from a SortGroupClause, sortref should be
* the SortGroupClause's SortGroupRef; otherwise zero.
*
+ * If rel is not NULL, it identifies a specific relation we're considering
+ * a path for, and indicates that child EC members for that relation can be
+ * considered. Otherwise child members are ignored. (See the comments for
+ * get_eclass_for_sort_expr.)
+ *
* create_it is TRUE if we should create any missing EquivalenceClass
* needed to represent the sort key. If it's FALSE, we return NULL if the
* sort key isn't already present in any EquivalenceClass.
@@ -237,6 +242,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
bool reverse_sort,
bool nulls_first,
Index sortref,
+ Relids rel,
bool create_it,
bool canonicalize)
{
@@ -268,7 +274,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
/* Now find or (optionally) create a matching EquivalenceClass */
eclass = get_eclass_for_sort_expr(root, expr, opfamilies,
opcintype, collation,
- sortref, create_it);
+ sortref, rel, create_it);
/* Fail if no EC and !create_it */
if (!eclass)
@@ -320,6 +326,7 @@ make_pathkey_from_sortop(PlannerInfo *root,
(strategy == BTGreaterStrategyNumber),
nulls_first,
sortref,
+ NULL,
create_it,
canonicalize);
}
@@ -546,6 +553,7 @@ build_index_pathkeys(PlannerInfo *root,
reverse_sort,
nulls_first,
0,
+ index->rel->relids,
false,
true);
@@ -636,6 +644,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
sub_member->em_datatype,
sub_eclass->ec_collation,
0,
+ rel->relids,
false);
/*
@@ -680,6 +689,9 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
Oid sub_expr_coll = sub_eclass->ec_collation;
ListCell *k;
+ if (sub_member->em_is_child)
+ continue; /* ignore children here */
+
foreach(k, sub_tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(k);
@@ -719,6 +731,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
sub_expr_type,
sub_expr_coll,
0,
+ rel->relids,
false);
/*
@@ -910,6 +923,7 @@ initialize_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
lefttype,
((OpExpr *) clause)->inputcollid,
0,
+ NULL,
true);
restrictinfo->right_ec =
get_eclass_for_sort_expr(root,
@@ -918,6 +932,7 @@ initialize_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
righttype,
((OpExpr *) clause)->inputcollid,
0,
+ NULL,
true);
}