public inbox for [email protected]
help / color / mirror / Atom feedFrom: Yuya Watari <[email protected]>
To: Alena Rybakina <[email protected]>
Cc: Andrei Lepikhov <[email protected]>
Cc: Ashutosh Bapat <[email protected]>
Cc: David Rowley <[email protected]>
Cc: Thom Brown <[email protected]>
Cc: Alvaro Herrera <[email protected]>
Cc: Zhang Mingli <[email protected]>
Cc: Tom Lane <[email protected]>
Cc: PostgreSQL Developers <[email protected]>
Subject: Re: [PoC] Reducing planning time when tables have many partitions
Date: Wed, 17 Jan 2024 18:33:42 +0900
Message-ID: <CAJ2pMkaaY4FgGSO2z9X_XOoqtLMUhyx6cPZFQbxmsjLgLpEd2Q@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <CAJ2pMkZwp545Fj=vyiyy=j=zPAEisb1=72PAU6vwyNc=Nx9vpA@mail.gmail.com>
<[email protected]>
<CAExHW5s-6WP2pQK_D9q92Ncbyvj+rDncofMJ4JGh0pkrh9FdRA@mail.gmail.com>
<CAJ2pMkaN57du6Yw+oomt44Ru+P2Hdp+vigexeYqUtbwyy=8SGA@mail.gmail.com>
<CAApHDvqp0UrCf9FLGyPWruiuU6BfPPquDxE2Wxw7ZS2S1FNH0A@mail.gmail.com>
<CAJ2pMka06YYV9UYx+NT1zkJO0ah+KHYWJ2vOmV2qGphfU5TjeQ@mail.gmail.com>
<CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com>
<[email protected]>
<CAJ2pMkaNzmvMUm9igQwRH0AAo39gsjnE1VXupPGyLR2T7ENnUQ@mail.gmail.com>
<[email protected]>
<CAJ2pMkbdVPqQcJ=vn4e7T+SmMGUVKvU2OcYZHFLD-paWEbaGZg@mail.gmail.com>
<[email protected]>
<CAJ2pMkbf+eWNEBTMdue03We2JtaQLvye7OsZB-v_EJmFQHo_gw@mail.gmail.com>
<CAJ2pMkYgCc=JsyQjMMKxVuSi1g6Ny8YZS6YSLsXrebA4mwy0OQ@mail.gmail.com>
<CAJ2pMkZGB4Yx1dCYkU_YRJgj2rcC0s+PWknqrszmsRPHGLqCgg@mail.gmail.com>
<[email protected]>
Hello Alena,
Thank you for your quick response, and I'm sorry for my delayed reply.
On Sun, Dec 17, 2023 at 12:41 AM Alena Rybakina
<[email protected]> wrote:
> I thought about this earlier and was worried that the index links of the equivalence classes might not be referenced correctly for outer joins,
> so I decided to just overwrite them and reset the previous ones.
Thank you for pointing this out. I have investigated this problem and
found a potential bug place. The code quoted below modifies
RestrictInfo's clause_relids. Here, our indexes, namely
eclass_source_indexes and eclass_derive_indexes, are based on
clause_relids, so they should be adjusted after the modification.
However, my patch didn't do that, so it may have missed some
references. The same problem occurs in places other than the quoted
one.
=====
/*
* Walker function for replace_varno()
*/
static bool
replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
{
...
else if (IsA(node, RestrictInfo))
{
RestrictInfo *rinfo = (RestrictInfo *) node;
...
if (bms_is_member(ctx->from, rinfo->clause_relids))
{
replace_varno((Node *) rinfo->clause, ctx->from, ctx->to);
replace_varno((Node *) rinfo->orclause, ctx->from, ctx->to);
rinfo->clause_relids = replace_relid(rinfo->clause_relids,
ctx->from, ctx->to);
...
}
...
}
...
}
=====
I have attached a new version of the patch, v23, to fix this problem.
v23-0006 adds a helper function called update_clause_relids(). This
function modifies RestrictInfo->clause_relids while adjusting its
related indexes. I have also attached a sanity check patch
(sanity-check.txt) to this email. This sanity check patch verifies
that there are no missing references between RestrictInfos and our
indexes. I don't intend to commit this patch, but it helps find
potential bugs. v23 passes this sanity check, but the v21 you
submitted before does not. This means that the adjustment by
update_clause_relids() is needed to prevent missing references after
modifying clause_relids. I'd appreciate your letting me know if v23
doesn't solve your concern.
One of the things I don't think is good about my approach is that it
adds some complexity to the code. In my approach, all modifications to
clause_relids must be done through the update_clause_relids()
function, but enforcing this rule is not so easy. In this sense, my
patch may need to be simplified more.
> this is due to the fact that I explained before: we zeroed the values indicated by the indexes,
> then this check is not correct either - since the zeroed value indicated by the index is correct.
> That's why I removed this check.
Thank you for letting me know. I fixed this in v23-0005 to adjust the
indexes in update_eclasses(). With this change, the assertion check
will be correct.
--
Best regards,
Yuya Watari
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 2da2a6c14e..334ea8d3bb 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1751,6 +1751,48 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
bms_free(ec->ec_source_indexes);
ec->ec_source_indexes = new_source_indexes;
ec->ec_relids = replace_relid(ec->ec_relids, from, to);
+
+#ifdef USE_ASSERT_CHECKING
+ /*
+ * Sanity check:
+ * Verify that there are no missing references between RestrictInfos and
+ * our indexes, namely eclass_source_indexes and eclass_derive_indexes.
+ */
+
+ foreach(lc, root->eq_sources)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+ int index;
+ int k;
+
+ if (rinfo == NULL)
+ continue;
+
+ index = list_cell_number(root->eq_sources, lc);
+ k = -1;
+ while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+ {
+ Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+ }
+ }
+
+ foreach(lc, root->eq_derives)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+ int index;
+ int k;
+
+ if (rinfo == NULL)
+ continue;
+
+ index = list_cell_number(root->eq_derives, lc);
+ k = -1;
+ while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+ {
+ Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+ }
+ }
+#endif
}
/*
Attachments:
[text/plain] sanity-check.txt (1.4K, ../CAJ2pMkaaY4FgGSO2z9X_XOoqtLMUhyx6cPZFQbxmsjLgLpEd2Q@mail.gmail.com/2-sanity-check.txt)
download | inline diff:
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 2da2a6c14e..334ea8d3bb 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1751,6 +1751,48 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
bms_free(ec->ec_source_indexes);
ec->ec_source_indexes = new_source_indexes;
ec->ec_relids = replace_relid(ec->ec_relids, from, to);
+
+#ifdef USE_ASSERT_CHECKING
+ /*
+ * Sanity check:
+ * Verify that there are no missing references between RestrictInfos and
+ * our indexes, namely eclass_source_indexes and eclass_derive_indexes.
+ */
+
+ foreach(lc, root->eq_sources)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+ int index;
+ int k;
+
+ if (rinfo == NULL)
+ continue;
+
+ index = list_cell_number(root->eq_sources, lc);
+ k = -1;
+ while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+ {
+ Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+ }
+ }
+
+ foreach(lc, root->eq_derives)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+ int index;
+ int k;
+
+ if (rinfo == NULL)
+ continue;
+
+ index = list_cell_number(root->eq_derives, lc);
+ k = -1;
+ while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+ {
+ Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+ }
+ }
+#endif
}
/*
[application/octet-stream] v23-0001-Speed-up-searches-for-child-EquivalenceMembers.patch (61.3K, ../CAJ2pMkaaY4FgGSO2z9X_XOoqtLMUhyx6cPZFQbxmsjLgLpEd2Q@mail.gmail.com/3-v23-0001-Speed-up-searches-for-child-EquivalenceMembers.patch)
download | inline diff:
From a95566fa43f01a4589cc300fe1985bc86abab6fd Mon Sep 17 00:00:00 2001
From: Yuya Watari <[email protected]>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v23 1/6] Speed up searches for child EquivalenceMembers
Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.
After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
contrib/postgres_fdw/postgres_fdw.c | 29 +-
src/backend/nodes/outfuncs.c | 2 +
src/backend/optimizer/path/equivclass.c | 414 ++++++++++++++++++----
src/backend/optimizer/path/indxpath.c | 40 ++-
src/backend/optimizer/path/pathkeys.c | 9 +-
src/backend/optimizer/plan/analyzejoins.c | 14 +-
src/backend/optimizer/plan/createplan.c | 57 +--
src/backend/optimizer/util/inherit.c | 14 +
src/backend/optimizer/util/relnode.c | 88 +++++
src/include/nodes/pathnodes.h | 26 ++
src/include/optimizer/pathnode.h | 18 +
src/include/optimizer/paths.h | 9 +-
12 files changed, 603 insertions(+), 117 deletions(-)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 142dcfc995..dbaefab1f6 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7773,13 +7773,27 @@ conversion_error_callback(void *arg)
EquivalenceMember *
find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
{
- ListCell *lc;
-
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+ Relids top_parent_rel_relids;
+ List *members;
+ bool modified;
+ int i;
- foreach(lc, ec->ec_members)
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
+
+ members = ec->ec_members;
+ modified = false;
+ for (i = 0; i < list_length(members); i++)
{
- EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+ EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
+
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ if (unlikely(top_parent_rel_relids != NULL) && !em->em_is_child &&
+ bms_is_subset(em->em_relids, top_parent_rel_relids))
+ add_child_rel_equivalences_to_list(root, ec, em,
+ rel->relids,
+ &members, &modified);
/*
* Note we require !bms_is_empty, else we'd accept constant
@@ -7791,6 +7805,8 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
is_foreign_expr(root, rel, em->em_expr))
return em;
}
+ if (unlikely(modified))
+ list_free(members);
return NULL;
}
@@ -7844,9 +7860,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
if (em->em_is_const)
continue;
- /* Ignore child members */
- if (em->em_is_child)
- continue;
+ /* Child members should not exist in ec_members */
+ Assert(!em->em_is_child);
/* Match if same expression (after stripping relabel) */
em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 296ba84518..97ab5d3770 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,6 +463,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
WRITE_NODE_FIELD(ec_opfamilies);
WRITE_OID_FIELD(ec_collation);
WRITE_NODE_FIELD(ec_members);
+ WRITE_NODE_FIELD(ec_norel_members);
+ WRITE_NODE_FIELD(ec_rel_members);
WRITE_NODE_FIELD(ec_sources);
WRITE_NODE_FIELD(ec_derives);
WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e86dfeaecd..d9eb4610ce 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
#include "utils/lsyscache.h"
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+ Expr *expr, Relids relids,
+ JoinDomain *jdomain,
+ EquivalenceMember *parent,
+ Oid datatype);
static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
Expr *expr, Relids relids,
JoinDomain *jdomain,
- EquivalenceMember *parent,
Oid datatype);
static bool is_exprlist_member(Expr *node, List *exprs);
static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -69,6 +73,12 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
static bool reconsider_full_join_clause(PlannerInfo *root,
OuterJoinClauseInfo *ojcinfo);
static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_transformed_child_version(PlannerInfo *root,
+ EquivalenceClass *ec,
+ EquivalenceMember *parent_em,
+ RelOptInfo *child_rel,
+ List **list,
+ bool *modified);
static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
Relids relids);
static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -342,6 +352,10 @@ process_equivalence(PlannerInfo *root,
* be found.
*/
ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+ ec1->ec_norel_members = list_concat(ec1->ec_norel_members,
+ ec2->ec_norel_members);
+ ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
+ ec2->ec_rel_members);
ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -355,6 +369,8 @@ process_equivalence(PlannerInfo *root,
root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
/* just to avoid debugging confusion w/ dangling pointers: */
ec2->ec_members = NIL;
+ ec2->ec_norel_members = NIL;
+ ec2->ec_rel_members = NIL;
ec2->ec_sources = NIL;
ec2->ec_derives = NIL;
ec2->ec_relids = NULL;
@@ -374,7 +390,7 @@ process_equivalence(PlannerInfo *root,
{
/* Case 3: add item2 to ec1 */
em2 = add_eq_member(ec1, item2, item2_relids,
- jdomain, NULL, item2_type);
+ jdomain, item2_type);
ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
ec1->ec_min_security = Min(ec1->ec_min_security,
restrictinfo->security_level);
@@ -391,7 +407,7 @@ process_equivalence(PlannerInfo *root,
{
/* Case 3: add item1 to ec2 */
em1 = add_eq_member(ec2, item1, item1_relids,
- jdomain, NULL, item1_type);
+ jdomain, item1_type);
ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
ec2->ec_min_security = Min(ec2->ec_min_security,
restrictinfo->security_level);
@@ -412,6 +428,8 @@ process_equivalence(PlannerInfo *root,
ec->ec_opfamilies = opfamilies;
ec->ec_collation = collation;
ec->ec_members = NIL;
+ ec->ec_norel_members = NIL;
+ ec->ec_rel_members = NIL;
ec->ec_sources = list_make1(restrictinfo);
ec->ec_derives = NIL;
ec->ec_relids = NULL;
@@ -423,9 +441,9 @@ process_equivalence(PlannerInfo *root,
ec->ec_max_security = restrictinfo->security_level;
ec->ec_merged = NULL;
em1 = add_eq_member(ec, item1, item1_relids,
- jdomain, NULL, item1_type);
+ jdomain, item1_type);
em2 = add_eq_member(ec, item2, item2_relids,
- jdomain, NULL, item2_type);
+ jdomain, item2_type);
root->eq_classes = lappend(root->eq_classes, ec);
@@ -511,11 +529,14 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
}
/*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. Note that child
+ * EquivalenceMembers should not be added to its parent EquivalenceClass.
*/
static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
- JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+ JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
{
EquivalenceMember *em = makeNode(EquivalenceMember);
@@ -526,6 +547,8 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
em->em_datatype = datatype;
em->em_jdomain = jdomain;
em->em_parent = parent;
+ em->em_child_relids = NULL;
+ em->em_child_joinrel_relids = NULL;
if (bms_is_empty(relids))
{
@@ -546,8 +569,34 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
{
ec->ec_relids = bms_add_members(ec->ec_relids, relids);
}
+
+ return em;
+}
+
+/*
+ * add_eq_member - build a new EquivalenceMember and add it to an EC
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+ JoinDomain *jdomain, Oid datatype)
+{
+ EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+ NULL, datatype);
+
ec->ec_members = lappend(ec->ec_members, em);
+ /*
+ * The exact set of relids in the expr for non-child EquivalenceMembers
+ * as what is given to us in 'relids' should be the same as the relids
+ * mentioned in the expression. See add_child_rel_equivalences.
+ */
+ /* XXX We need PlannerInfo to use the following assertion */
+ /* Assert(bms_equal(pull_varnos(root, (Node *) expr), relids)); */
+ if (bms_is_empty(relids))
+ ec->ec_norel_members = lappend(ec->ec_norel_members, em);
+ else
+ ec->ec_rel_members = lappend(ec->ec_rel_members, em);
+
return em;
}
@@ -599,6 +648,17 @@ get_eclass_for_sort_expr(PlannerInfo *root,
EquivalenceMember *newem;
ListCell *lc1;
MemoryContext oldcontext;
+ Relids top_parent_rel;
+
+ /*
+ * First, we translate the given Relids to their top-level parents. This is
+ * required because an EquivalenceClass contains only parent
+ * EquivalenceMembers, and we have to translate top-level ones to get child
+ * members. We can skip such translations if we now see top-level ones,
+ * i.e., when top_parent_rel is NULL. See the find_relids_top_parents()'s
+ * definition for more details.
+ */
+ top_parent_rel = find_relids_top_parents(root, rel);
/*
* Ensure the expression exposes the correct type and collation.
@@ -617,7 +677,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
foreach(lc1, root->eq_classes)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
- ListCell *lc2;
+ List *members;
+ bool modified;
+ int i;
/*
* Never match to a volatile EC, except when we are looking at another
@@ -632,16 +694,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
if (!equal(opfamilies, cur_ec->ec_opfamilies))
continue;
- foreach(lc2, cur_ec->ec_members)
+ /*
+ * When we have to see child EquivalenceMembers, we get and add them to
+ * 'members'. We cannot use foreach() because the 'members' may be
+ * modified during iteration.
+ */
+ members = cur_ec->ec_members;
+ modified = false;
+ for (i = 0; i < list_length(members); i++)
{
- EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+ EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+ /*
+ * If child EquivalenceMembers may match the request, we add and
+ * iterate over them.
+ */
+ if (unlikely(top_parent_rel != NULL) && !cur_em->em_is_child &&
+ bms_equal(cur_em->em_relids, top_parent_rel))
+ add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel,
+ &members, &modified);
/*
* Ignore child members unless they match the request.
*/
- if (cur_em->em_is_child &&
- !bms_equal(cur_em->em_relids, rel))
- continue;
+ /*
+ * If this EquivalenceMember is a child, i.e., translated above,
+ * it should match the request. We cannot assert this if a request
+ * is bms_is_subset().
+ */
+ Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
/*
* Match constants only within the same JoinDomain (see
@@ -654,6 +735,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
equal(expr, cur_em->em_expr))
return cur_ec; /* Match! */
}
+ if (unlikely(modified))
+ list_free(members);
}
/* No match; does caller want a NULL result? */
@@ -671,6 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
newec->ec_opfamilies = list_copy(opfamilies);
newec->ec_collation = collation;
newec->ec_members = NIL;
+ newec->ec_norel_members = NIL;
+ newec->ec_rel_members = NIL;
newec->ec_sources = NIL;
newec->ec_derives = NIL;
newec->ec_relids = NULL;
@@ -691,7 +776,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
expr_relids = pull_varnos(root, (Node *) expr);
newem = add_eq_member(newec, copyObject(expr), expr_relids,
- jdomain, NULL, opcintype);
+ jdomain, opcintype);
/*
* add_eq_member doesn't check for volatile functions, set-returning
@@ -757,19 +842,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
* Child EC members are ignored unless they belong to given 'relids'.
*/
EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
Expr *expr,
Relids relids)
{
- ListCell *lc;
+ Relids top_parent_relids;
+ List *members;
+ bool modified;
+ int i;
+
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ top_parent_relids = find_relids_top_parents(root, relids);
/* We ignore binary-compatible relabeling on both ends */
while (expr && IsA(expr, RelabelType))
expr = ((RelabelType *) expr)->arg;
- foreach(lc, ec->ec_members)
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ members = ec->ec_members;
+ modified = false;
+ for (i = 0; i < list_length(members); i++)
{
- EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+ EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
Expr *emexpr;
/*
@@ -779,6 +873,12 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
if (em->em_is_const)
continue;
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+ bms_is_subset(em->em_relids, top_parent_relids))
+ add_child_rel_equivalences_to_list(root, ec, em, relids,
+ &members, &modified);
+
/*
* Ignore child members unless they belong to the requested rel.
*/
@@ -796,6 +896,8 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
if (equal(emexpr, expr))
return em;
}
+ if (unlikely(modified))
+ list_free(members);
return NULL;
}
@@ -828,11 +930,20 @@ find_computable_ec_member(PlannerInfo *root,
Relids relids,
bool require_parallel_safe)
{
- ListCell *lc;
+ Relids top_parent_relids;
+ List *members;
+ bool modified;
+ int i;
- foreach(lc, ec->ec_members)
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ top_parent_relids = find_relids_top_parents(root, relids);
+
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ members = ec->ec_members;
+ modified = false;
+ for (i = 0; i < list_length(members); i++)
{
- EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+ EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
List *exprvars;
ListCell *lc2;
@@ -843,6 +954,12 @@ find_computable_ec_member(PlannerInfo *root,
if (em->em_is_const)
continue;
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+ bms_is_subset(em->em_relids, top_parent_relids))
+ add_child_rel_equivalences_to_list(root, ec, em, relids,
+ &members, &modified);
+
/*
* Ignore child members unless they belong to the requested rel.
*/
@@ -876,6 +993,8 @@ find_computable_ec_member(PlannerInfo *root,
return em; /* found usable expression */
}
+ if (unlikely(modified))
+ list_free(members);
return NULL;
}
@@ -939,7 +1058,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
{
Expr *targetexpr = (Expr *) lfirst(lc);
- em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+ em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
if (!em)
continue;
@@ -1138,7 +1257,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
* machinery might be able to exclude relations on the basis of generated
* "var = const" equalities, but "var = param" won't work for that.
*/
- foreach(lc, ec->ec_members)
+ foreach(lc, ec->ec_norel_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
@@ -1222,7 +1341,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
prev_ems = (EquivalenceMember **)
palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
- foreach(lc, ec->ec_members)
+ foreach(lc, ec->ec_rel_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
int relid;
@@ -1559,7 +1678,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
List *new_members = NIL;
List *outer_members = NIL;
List *inner_members = NIL;
- ListCell *lc1;
+ Relids top_parent_join_relids;
+ List *members;
+ bool modified;
+ int i;
+
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ top_parent_join_relids = find_relids_top_parents(root, join_relids);
/*
* First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1695,19 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
* as well as to at least one input member, plus enforce at least one
* outer-rel member equal to at least one inner-rel member.
*/
- foreach(lc1, ec->ec_members)
+
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ members = ec->ec_rel_members;
+ modified = false;
+ for (i = 0; i < list_length(members); i++)
{
- EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+ EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ if (unlikely(top_parent_join_relids != NULL) && !cur_em->em_is_child &&
+ bms_is_subset(cur_em->em_relids, top_parent_join_relids))
+ add_child_rel_equivalences_to_list(root, ec, cur_em, join_relids,
+ &members, &modified);
/*
* We don't need to check explicitly for child EC members. This test
@@ -1589,6 +1724,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
else
new_members = lappend(new_members, cur_em);
}
+ if (unlikely(modified))
+ list_free(members);
/*
* First, select the joinclause if needed. We can equate any one outer
@@ -1606,6 +1743,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
Oid best_eq_op = InvalidOid;
int best_score = -1;
RestrictInfo *rinfo;
+ ListCell *lc1;
foreach(lc1, outer_members)
{
@@ -1680,6 +1818,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
List *old_members = list_concat(outer_members, inner_members);
EquivalenceMember *prev_em = NULL;
RestrictInfo *rinfo;
+ ListCell *lc1;
/* For now, arbitrarily take the first old_member as the one to use */
if (old_members)
@@ -2177,7 +2316,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
* constant before we can decide to throw away the outer-join clause.
*/
match = false;
- foreach(lc2, cur_ec->ec_members)
+ foreach(lc2, cur_ec->ec_norel_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
Oid eq_op;
@@ -2285,7 +2424,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
* the COALESCE arguments?
*/
match = false;
- foreach(lc2, cur_ec->ec_members)
+ foreach(lc2, cur_ec->ec_rel_members)
{
coal_em = (EquivalenceMember *) lfirst(lc2);
Assert(!coal_em->em_is_child); /* no children yet */
@@ -2330,7 +2469,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
* decide to throw away the outer-join clause.
*/
matchleft = matchright = false;
- foreach(lc2, cur_ec->ec_members)
+ foreach(lc2, cur_ec->ec_norel_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
Oid eq_op;
@@ -2385,6 +2524,10 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
if (matchleft && matchright)
{
cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+ Assert(!bms_is_empty(coal_em->em_relids));
+ /* XXX performance of list_delete_ptr()?? */
+ cur_ec->ec_rel_members = list_delete_ptr(cur_ec->ec_rel_members,
+ coal_em);
return true;
}
@@ -2455,8 +2598,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
- if (em->em_is_child)
- continue; /* ignore children here */
+ /* Child members should not exist in ec_members */
+ Assert(!em->em_is_child);
if (equal(item1, em->em_expr))
item1member = true;
else if (equal(item2, em->em_expr))
@@ -2523,13 +2666,13 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
continue;
/* Note: it seems okay to match to "broken" eclasses here */
- foreach(lc2, ec->ec_members)
+ foreach(lc2, ec->ec_rel_members)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
Var *var;
- if (em->em_is_child)
- continue; /* ignore children here */
+ /* Child members should not exist in ec_members */
+ Assert(!em->em_is_child);
/* EM must be a Var, possibly with RelabelType */
var = (Var *) em->em_expr;
@@ -2626,6 +2769,7 @@ add_child_rel_equivalences(PlannerInfo *root,
Relids top_parent_relids = child_rel->top_parent_relids;
Relids child_relids = child_rel->relids;
int i;
+ ListCell *lc;
/*
* EC merging should be complete already, so we can use the parent rel's
@@ -2638,7 +2782,6 @@ add_child_rel_equivalences(PlannerInfo *root,
while ((i = bms_next_member(parent_rel->eclass_indexes, i)) >= 0)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
- int num_members;
/*
* If this EC contains a volatile expression, then generating child
@@ -2651,15 +2794,9 @@ add_child_rel_equivalences(PlannerInfo *root,
/* Sanity check eclass_indexes only contain ECs for parent_rel */
Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
- /*
- * We don't use foreach() here because there's no point in scanning
- * newly-added child members, so we can stop after the last
- * pre-existing EC member.
- */
- num_members = list_length(cur_ec->ec_members);
- for (int pos = 0; pos < num_members; pos++)
+ foreach(lc, cur_ec->ec_rel_members)
{
- EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+ EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
if (cur_em->em_is_const)
continue; /* ignore consts here */
@@ -2672,8 +2809,8 @@ add_child_rel_equivalences(PlannerInfo *root,
* combinations of children. (But add_child_join_rel_equivalences
* may add targeted combinations for partitionwise-join purposes.)
*/
- if (cur_em->em_is_child)
- continue; /* ignore children here */
+ /* Child members should not exist in ec_members */
+ Assert(!cur_em->em_is_child);
/*
* Consider only members that reference and can be computed at
@@ -2689,6 +2826,7 @@ add_child_rel_equivalences(PlannerInfo *root,
/* OK, generate transformed child version */
Expr *child_expr;
Relids new_relids;
+ EquivalenceMember *child_em;
if (parent_rel->reloptkind == RELOPT_BASEREL)
{
@@ -2718,9 +2856,20 @@ add_child_rel_equivalences(PlannerInfo *root,
top_parent_relids);
new_relids = bms_add_members(new_relids, child_relids);
- (void) add_eq_member(cur_ec, child_expr, new_relids,
- cur_em->em_jdomain,
- cur_em, cur_em->em_datatype);
+ child_em = make_eq_member(cur_ec, child_expr, new_relids,
+ cur_em->em_jdomain,
+ cur_em, cur_em->em_datatype);
+ child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
+ child_em);
+
+ /*
+ * We save the knowledge that 'child_em' can be translated from
+ * 'child_rel'. This knowledge is useful for
+ * add_transformed_child_version() to find child members from the
+ * given Relids.
+ */
+ cur_em->em_child_relids = bms_add_member(cur_em->em_child_relids,
+ child_rel->relid);
/* Record this EC index for the child rel */
child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
@@ -2749,6 +2898,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
Relids child_relids = child_joinrel->relids;
Bitmapset *matching_ecs;
MemoryContext oldcontext;
+ ListCell *lc;
int i;
Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2770,7 +2920,6 @@ add_child_join_rel_equivalences(PlannerInfo *root,
while ((i = bms_next_member(matching_ecs, i)) >= 0)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
- int num_members;
/*
* If this EC contains a volatile expression, then generating child
@@ -2783,15 +2932,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
/* Sanity check on get_eclass_indexes_for_relids result */
Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
- /*
- * We don't use foreach() here because there's no point in scanning
- * newly-added child members, so we can stop after the last
- * pre-existing EC member.
- */
- num_members = list_length(cur_ec->ec_members);
- for (int pos = 0; pos < num_members; pos++)
+ foreach(lc, cur_ec->ec_rel_members)
{
- EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+ EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
if (cur_em->em_is_const)
continue; /* ignore consts here */
@@ -2800,8 +2943,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
* We consider only original EC members here, not
* already-transformed child members.
*/
- if (cur_em->em_is_child)
- continue; /* ignore children here */
+ /* Child members should not exist in ec_members */
+ Assert(!cur_em->em_is_child);
/*
* We may ignore expressions that reference a single baserel,
@@ -2816,6 +2959,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
/* Yes, generate transformed child version */
Expr *child_expr;
Relids new_relids;
+ EquivalenceMember *child_em;
if (parent_joinrel->reloptkind == RELOPT_JOINREL)
{
@@ -2846,9 +2990,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
top_parent_relids);
new_relids = bms_add_members(new_relids, child_relids);
- (void) add_eq_member(cur_ec, child_expr, new_relids,
- cur_em->em_jdomain,
- cur_em, cur_em->em_datatype);
+ child_em = make_eq_member(cur_ec, child_expr, new_relids,
+ cur_em->em_jdomain,
+ cur_em, cur_em->em_datatype);
+ child_joinrel->eclass_child_members =
+ lappend(child_joinrel->eclass_child_members, child_em);
+
+ /*
+ * We save the knowledge that 'child_em' can be translated from
+ * 'child_joinrel'. This knowledge is useful for
+ * add_transformed_child_version() to find child members from the
+ * given Relids.
+ */
+ cur_em->em_child_joinrel_relids =
+ bms_add_member(cur_em->em_child_joinrel_relids,
+ child_joinrel->join_rel_list_index);
}
}
}
@@ -2856,6 +3012,106 @@ add_child_join_rel_equivalences(PlannerInfo *root,
MemoryContextSwitchTo(oldcontext);
}
+/*
+ * add_transformed_child_version
+ * Add a transformed EquivalenceMember referencing the given child_rel to
+ * the list.
+ *
+ * This function is expected to be called only from
+ * add_child_rel_equivalences_to_list().
+ */
+static void
+add_transformed_child_version(PlannerInfo *root,
+ EquivalenceClass *ec,
+ EquivalenceMember *parent_em,
+ RelOptInfo *child_rel,
+ List **list,
+ bool *modified)
+{
+ ListCell *lc;
+
+ foreach(lc, child_rel->eclass_child_members)
+ {
+ EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+ /* Skip unwanted EquivalenceMembers */
+ if (child_em->em_parent != parent_em)
+ continue;
+
+ /*
+ * If this is the first time the given list has been modified, we need to
+ * make a copy of it.
+ */
+ if (!*modified)
+ {
+ *list = list_copy(*list);
+ *modified = true;
+ }
+
+ /* Add this child EquivalenceMember to the list */
+ *list = lappend(*list, child_em);
+ }
+}
+
+/*
+ * add_child_rel_equivalences_to_list
+ * Add transformed EquivalenceMembers referencing child rels in the given
+ * child_relids to the list.
+ *
+ * The transformation is done in add_transformed_child_version().
+ *
+ * This function will not change the original *list but will always make a copy
+ * of it when we need to add transformed members. *modified will be true if the
+ * *list is replaced with a newly allocated one. In such a case, the caller can
+ * (or must) free the *list.
+ */
+void
+add_child_rel_equivalences_to_list(PlannerInfo *root,
+ EquivalenceClass *ec,
+ EquivalenceMember *parent_em,
+ Relids child_relids,
+ List **list,
+ bool *modified)
+{
+ int i;
+ Relids matching_relids;
+
+ /* The given EquivalenceMember should be parent */
+ Assert(!parent_em->em_is_child);
+
+ /*
+ * First, we translate simple rels.
+ */
+ matching_relids = bms_intersect(parent_em->em_child_relids,
+ child_relids);
+ i = -1;
+ while ((i = bms_next_member(matching_relids, i)) >= 0)
+ {
+ /* Add transformed child version for this child rel */
+ RelOptInfo *child_rel = root->simple_rel_array[i];
+
+ Assert(child_rel != NULL);
+ add_transformed_child_version(root, ec, parent_em, child_rel,
+ list, modified);
+ }
+ bms_free(matching_relids);
+
+ /*
+ * Next, we try to translate join rels.
+ */
+ i = -1;
+ while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+ {
+ /* Add transformed child version for this child join rel */
+ RelOptInfo *child_joinrel =
+ list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+ Assert(child_joinrel != NULL);
+ add_transformed_child_version(root, ec, parent_em, child_joinrel,
+ list, modified);
+ }
+}
+
/*
* generate_implied_equalities_for_column
@@ -2889,7 +3145,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
{
List *result = NIL;
bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
- Relids parent_relids;
+ Relids parent_relids, top_parent_rel_relids;
int i;
/* Should be OK to rely on eclass_indexes */
@@ -2898,6 +3154,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
/* Indexes are available only on base or "other" member relations. */
Assert(IS_SIMPLE_REL(rel));
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
+
/* If it's a child rel, we'll need to know what its parent(s) are */
if (is_child_rel)
parent_relids = find_childrel_parents(root, rel);
@@ -2910,6 +3169,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
EquivalenceMember *cur_em;
ListCell *lc2;
+ List *members;
+ bool modified;
+ int j;
/* Sanity check eclass_indexes only contain ECs for rel */
Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2931,15 +3193,25 @@ generate_implied_equalities_for_column(PlannerInfo *root,
* corner cases, so for now we live with just reporting the first
* match. See also get_eclass_for_sort_expr.)
*/
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ members = cur_ec->ec_rel_members;
+ modified = false;
cur_em = NULL;
- foreach(lc2, cur_ec->ec_members)
+ for (j = 0; j < list_length(members); j++)
{
- cur_em = (EquivalenceMember *) lfirst(lc2);
+ cur_em = list_nth_node(EquivalenceMember, members, j);
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ if (unlikely(top_parent_rel_relids != NULL) && !cur_em->em_is_child &&
+ bms_equal(cur_em->em_relids, top_parent_rel_relids))
+ add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel->relids,
+ &members, &modified);
if (bms_equal(cur_em->em_relids, rel->relids) &&
callback(root, rel, cur_ec, cur_em, callback_arg))
break;
cur_em = NULL;
}
+ if (unlikely(modified))
+ list_free(members);
if (!cur_em)
continue;
@@ -2954,8 +3226,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
Oid eq_op;
RestrictInfo *rinfo;
- if (other_em->em_is_child)
- continue; /* ignore children here */
+ /* Child members should not exist in ec_members */
+ Assert(!other_em->em_is_child);
/* Make sure it'll be a join to a different rel */
if (other_em == cur_em ||
@@ -3173,8 +3445,8 @@ eclass_useful_for_merging(PlannerInfo *root,
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
- if (cur_em->em_is_child)
- continue; /* ignore children here */
+ /* Child members should not exist in ec_members */
+ Assert(!cur_em->em_is_child);
if (!bms_overlap(cur_em->em_relids, relids))
return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 32c6a8bbdc..1f7438a619 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,7 +184,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
IndexOptInfo *index,
Oid expr_op,
bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
List **orderby_clauses_p,
List **clause_columns_p);
static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -980,7 +980,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
* query_pathkeys will allow an incremental sort to be considered on
* the index's partially sorted results.
*/
- match_pathkeys_to_index(index, root->query_pathkeys,
+ match_pathkeys_to_index(root, index, root->query_pathkeys,
&orderbyclauses,
&orderbyclausecols);
if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3071,12 +3071,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
* item in the given 'pathkeys' list.
*/
static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
List **orderby_clauses_p,
List **clause_columns_p)
{
+ Relids top_parent_index_relids;
ListCell *lc1;
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
*orderby_clauses_p = NIL; /* set default results */
*clause_columns_p = NIL;
@@ -3088,7 +3092,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
{
PathKey *pathkey = (PathKey *) lfirst(lc1);
bool found = false;
- ListCell *lc2;
+ List *members;
+ bool modified;
+ int i;
/* Pathkey must request default sort order for the target opfamily */
@@ -3108,15 +3114,33 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
* 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)
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ members = pathkey->pk_eclass->ec_members;
+ modified = false;
+ for (i = 0; i < list_length(members); i++)
{
- EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+ EquivalenceMember *member = list_nth_node(EquivalenceMember, members, i);
int indexcol;
+ /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+ if (unlikely(top_parent_index_relids != NULL) && !member->em_is_child &&
+ bms_equal(member->em_relids, top_parent_index_relids))
+ add_child_rel_equivalences_to_list(root, pathkey->pk_eclass, member,
+ index->rel->relids,
+ &members, &modified);
+
/* No possibility of match if it references other relations */
- if (!bms_equal(member->em_relids, index->rel->relids))
+ if (member->em_is_child ||
+ !bms_equal(member->em_relids, index->rel->relids))
continue;
+ /*
+ * If this EquivalenceMember is a child, i.e., translated above,
+ * it should match the request. We cannot assert this if a request
+ * is bms_is_subset().
+ */
+ Assert(bms_equal(member->em_relids, index->rel->relids));
+
/*
* We allow any column of the index to match each pathkey; they
* don't have to match left-to-right as you might expect. This is
@@ -3145,6 +3169,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
if (found) /* don't want to look at remaining members */
break;
}
+ if (unlikely(modified))
+ list_free(members);
/*
* Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index ca94a31f71..aac64c4124 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -952,8 +952,8 @@ 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 */
+ /* Child members should not exist in ec_members */
+ Assert(!sub_member->em_is_child);
foreach(k, subquery_tlist)
{
@@ -1479,8 +1479,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+ /* Child members should not exist in ec_members */
+ Assert(!em->em_is_child);
+
/* Potential future join partner? */
- if (!em->em_is_const && !em->em_is_child &&
+ if (!em->em_is_const &&
!bms_overlap(em->em_relids, joinrel->relids))
score++;
}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 7dcb74572a..f0735f3c41 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -61,7 +61,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
SpecialJoinInfo *sjinfo);
static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
int relid, int ojrelid);
static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -580,7 +580,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
if (bms_is_member(relid, ec->ec_relids) ||
bms_is_member(ojrelid, ec->ec_relids))
- remove_rel_from_eclass(ec, relid, ojrelid);
+ remove_rel_from_eclass(root, ec, relid, ojrelid);
}
/*
@@ -665,7 +665,8 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
* level(s).
*/
static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+ int ojrelid)
{
ListCell *lc;
@@ -689,7 +690,14 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
if (bms_is_empty(cur_em->em_relids))
+ {
ec->ec_members = foreach_delete_current(ec->ec_members, lc);
+ /* XXX performance of list_delete_ptr()?? */
+ ec->ec_norel_members = list_delete_ptr(ec->ec_norel_members,
+ cur_em);
+ ec->ec_rel_members = list_delete_ptr(ec->ec_rel_members,
+ cur_em);
+ }
}
}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ca619eab94..dce42ada81 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,7 +260,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
int numCols, int nPresortedCols,
AttrNumber *sortColIdx, Oid *sortOperators,
Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+ Plan *lefttree,
+ List *pathkeys,
Relids relids,
const AttrNumber *reqColIdx,
bool adjust_tlist_in_place,
@@ -269,9 +271,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
Oid **p_sortOperators,
Oid **p_collations,
bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+ Plan *lefttree,
+ List *pathkeys,
Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
List *pathkeys, Relids relids, int nPresortedCols);
static Sort *make_sort_from_groupcols(List *groupcls,
AttrNumber *grpColIdx,
@@ -293,7 +297,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
Plan *lefttree);
static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
List *pathkeys, int numCols);
static Gather *make_gather(List *qptlist, List *qpqual,
int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1280,7 +1284,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
* function result; it must be the same plan node. However, we then
* need to detect whether any tlist entries were added.
*/
- (void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+ (void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
best_path->path.parent->relids,
NULL,
true,
@@ -1324,7 +1328,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
* don't need an explicit sort, to make sure they are returning
* the same sort key columns the Append expects.
*/
- subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+ subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
subpath->parent->relids,
nodeSortColIdx,
false,
@@ -1465,7 +1469,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
* function result; it must be the same plan node. However, we then need
* to detect whether any tlist entries were added.
*/
- (void) prepare_sort_from_pathkeys(plan, pathkeys,
+ (void) prepare_sort_from_pathkeys(root, plan, pathkeys,
best_path->path.parent->relids,
NULL,
true,
@@ -1496,7 +1500,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
/* Compute sort column info, and adjust subplan's tlist as needed */
- subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+ subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
subpath->parent->relids,
node->sortColIdx,
false,
@@ -1975,7 +1979,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
Assert(pathkeys != NIL);
/* Compute sort column info, and adjust subplan's tlist as needed */
- subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+ subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
best_path->subpath->parent->relids,
gm_plan->sortColIdx,
false,
@@ -2194,7 +2198,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
* relids. Thus, if this sort path is based on a child relation, we must
* pass its relids.
*/
- plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+ plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
IS_OTHER_REL(best_path->subpath->parent) ?
best_path->path.parent->relids : NULL);
@@ -2218,7 +2222,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
/* See comments in create_sort_plan() above */
subplan = create_plan_recurse(root, best_path->spath.subpath,
flags | CP_SMALL_TLIST);
- plan = make_incrementalsort_from_pathkeys(subplan,
+ plan = make_incrementalsort_from_pathkeys(root, subplan,
best_path->spath.path.pathkeys,
IS_OTHER_REL(best_path->spath.subpath->parent) ?
best_path->spath.path.parent->relids : NULL,
@@ -2287,7 +2291,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
subplan = create_plan_recurse(root, best_path->subpath,
flags | CP_LABEL_TLIST);
- plan = make_unique_from_pathkeys(subplan,
+ plan = make_unique_from_pathkeys(root, subplan,
best_path->path.pathkeys,
best_path->numkeys);
@@ -4498,7 +4502,7 @@ create_mergejoin_plan(PlannerInfo *root,
if (best_path->outersortkeys)
{
Relids outer_relids = outer_path->parent->relids;
- Sort *sort = make_sort_from_pathkeys(outer_plan,
+ Sort *sort = make_sort_from_pathkeys(root, outer_plan,
best_path->outersortkeys,
outer_relids);
@@ -4512,7 +4516,7 @@ create_mergejoin_plan(PlannerInfo *root,
if (best_path->innersortkeys)
{
Relids inner_relids = inner_path->parent->relids;
- Sort *sort = make_sort_from_pathkeys(inner_plan,
+ Sort *sort = make_sort_from_pathkeys(root, inner_plan,
best_path->innersortkeys,
inner_relids);
@@ -6133,7 +6137,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
* or a Result stacked atop lefttree).
*/
static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
Relids relids,
const AttrNumber *reqColIdx,
bool adjust_tlist_in_place,
@@ -6200,7 +6204,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
if (tle)
{
- em = find_ec_member_matching_expr(ec, tle->expr, relids);
+ em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
if (em)
{
/* found expr at right place in tlist */
@@ -6228,7 +6232,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
foreach(j, tlist)
{
tle = (TargetEntry *) lfirst(j);
- em = find_ec_member_matching_expr(ec, tle->expr, relids);
+ em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
if (em)
{
/* found expr already in tlist */
@@ -6244,7 +6248,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
/*
* No matching tlist item; look for a computable expression.
*/
- em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+ em = find_computable_ec_member(root, ec, tlist, relids, false);
if (!em)
elog(ERROR, "could not find pathkey item to sort");
pk_datatype = em->em_datatype;
@@ -6315,7 +6319,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
* 'relids' is the set of relations required by prepare_sort_from_pathkeys()
*/
static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+ Relids relids)
{
int numsortkeys;
AttrNumber *sortColIdx;
@@ -6324,7 +6329,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
bool *nullsFirst;
/* Compute sort column info, and adjust lefttree as needed */
- lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+ lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
relids,
NULL,
false,
@@ -6350,8 +6355,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
* 'nPresortedCols' is the number of presorted columns in input tuples
*/
static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
- Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+ List *pathkeys, Relids relids,
+ int nPresortedCols)
{
int numsortkeys;
AttrNumber *sortColIdx;
@@ -6360,7 +6366,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
bool *nullsFirst;
/* Compute sort column info, and adjust lefttree as needed */
- lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+ lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
relids,
NULL,
false,
@@ -6717,7 +6723,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
* as above, but use pathkeys to identify the sort columns and semantics
*/
static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+ int numCols)
{
Unique *node = makeNode(Unique);
Plan *plan = &node->plan;
@@ -6780,7 +6787,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
foreach(j, plan->targetlist)
{
tle = (TargetEntry *) lfirst(j);
- em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+ em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
if (em)
{
/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 5c7acf8a90..3501fbbeed 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -461,6 +461,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
RelationGetRelid(parentrel);
Oid childOID = RelationGetRelid(childrel);
RangeTblEntry *childrte;
+ Index topParentRTindex;
Index childRTindex;
AppendRelInfo *appinfo;
TupleDesc child_tupdesc;
@@ -568,6 +569,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
Assert(root->append_rel_array[childRTindex] == NULL);
root->append_rel_array[childRTindex] = appinfo;
+ /*
+ * Find a top parent rel's index and save it to top_parent_relid_array.
+ */
+ if (root->top_parent_relid_array == NULL)
+ root->top_parent_relid_array =
+ (Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+ Assert(root->top_parent_relid_array[childRTindex] == 0);
+ topParentRTindex = parentRTindex;
+ while (root->append_rel_array[topParentRTindex] != NULL &&
+ root->append_rel_array[topParentRTindex]->parent_relid != 0)
+ topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+ root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
/*
* Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
*/
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 22d01cef5b..b8185d0dda 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -123,11 +123,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
if (root->append_rel_list == NIL)
{
root->append_rel_array = NULL;
+ root->top_parent_relid_array = NULL;
return;
}
root->append_rel_array = (AppendRelInfo **)
palloc0(size * sizeof(AppendRelInfo *));
+ root->top_parent_relid_array = (Index *)
+ palloc0(size * sizeof(Index));
/*
* append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +151,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
root->append_rel_array[child_relid] = appinfo;
}
+
+ /*
+ * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+ * in the last foreach loop because there may be multi-level parent-child
+ * relations.
+ */
+ for (int i = 0; i < size; i++)
+ {
+ int top_parent_relid;
+
+ if (root->append_rel_array[i] == NULL)
+ continue;
+
+ top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+ while (root->append_rel_array[top_parent_relid] != NULL &&
+ root->append_rel_array[top_parent_relid]->parent_relid != 0)
+ top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+ root->top_parent_relid_array[i] = top_parent_relid;
+ }
}
/*
@@ -175,11 +199,23 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
if (root->append_rel_array)
+ {
root->append_rel_array =
repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+ root->top_parent_relid_array =
+ repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+ }
else
+ {
root->append_rel_array =
palloc0_array(AppendRelInfo *, new_size);
+ /*
+ * We do not allocate top_parent_relid_array here so that
+ * find_relids_top_parents() can early find all of the given Relids are
+ * top-level.
+ */
+ root->top_parent_relid_array = NULL;
+ }
root->simple_rel_array_size = new_size;
}
@@ -233,6 +269,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->subplan_params = NIL;
rel->rel_parallel_workers = -1; /* set up in get_relation_info */
rel->amflags = 0;
+ rel->eclass_child_members = NIL;
rel->serverid = InvalidOid;
if (rte->rtekind == RTE_RELATION)
{
@@ -621,6 +658,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
/* GEQO requires us to append the new joinrel to the end of the list! */
root->join_rel_list = lappend(root->join_rel_list, joinrel);
+ /*
+ * Store the index of this joinrel to use in
+ * add_child_join_rel_equivalences().
+ */
+ joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
/* store it into the auxiliary hashtable if there is one. */
if (root->join_rel_hash)
{
@@ -732,6 +775,7 @@ build_join_rel(PlannerInfo *root,
joinrel->subplan_params = NIL;
joinrel->rel_parallel_workers = -1;
joinrel->amflags = 0;
+ joinrel->eclass_child_members = NIL;
joinrel->serverid = InvalidOid;
joinrel->userid = InvalidOid;
joinrel->useridiscurrent = false;
@@ -928,6 +972,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
joinrel->subroot = NULL;
joinrel->subplan_params = NIL;
joinrel->amflags = 0;
+ joinrel->eclass_child_members = NIL;
joinrel->serverid = InvalidOid;
joinrel->userid = InvalidOid;
joinrel->useridiscurrent = false;
@@ -1492,6 +1537,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
upperrel->cheapest_total_path = NULL;
upperrel->cheapest_unique_path = NULL;
upperrel->cheapest_parameterized_paths = NIL;
+ upperrel->eclass_child_members = NIL; /* XXX Is this required? */
root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
@@ -1532,6 +1578,48 @@ find_childrel_parents(PlannerInfo *root, RelOptInfo *rel)
}
+/*
+ * find_relids_top_parents_slow
+ * Slow path of find_relids_top_parents() macro.
+ *
+ * Do not call this directly; use the macro instead. See the macro comment for
+ * more details.
+ */
+Relids
+find_relids_top_parents_slow(PlannerInfo *root, Relids relids)
+{
+ Index *top_parent_relid_array = root->top_parent_relid_array;
+ Relids result;
+ bool is_top_parent;
+ int i;
+
+ /* This function should be called in the slow path */
+ Assert(top_parent_relid_array != NULL);
+
+ result = NULL;
+ is_top_parent = true;
+ i = -1;
+ while ((i = bms_next_member(relids, i)) >= 0)
+ {
+ int top_parent_relid = (int) top_parent_relid_array[i];
+
+ if (top_parent_relid == 0)
+ top_parent_relid = i; /* 'i' has no parents, so add itself */
+ else if (top_parent_relid != i)
+ is_top_parent = false;
+ result = bms_add_member(result, top_parent_relid);
+ }
+
+ if (is_top_parent)
+ {
+ bms_free(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+
/*
* get_baserel_parampathinfo
* Get the ParamPathInfo for a parameterized path for a base relation,
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b9713ec9aa..8c55a2bcf0 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -245,6 +245,14 @@ struct PlannerInfo
*/
struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
+ /*
+ * append_rel_array is the same length as simple_rel_array and holds the
+ * top-level parent indexes of the corresponding rels within
+ * simple_rel_array. The element can be zero if the rel has no parents,
+ * i.e., is itself in a top-level.
+ */
+ Index *top_parent_relid_array pg_node_attr(read_write_ignore);
+
/*
* all_baserels is a Relids set of all base relids (but not joins or
* "other" rels) in the query. This is computed in deconstruct_jointree.
@@ -936,6 +944,17 @@ typedef struct RelOptInfo
/* Bitmask of optional features supported by the table AM */
uint32 amflags;
+ /*
+ * information about a join rel
+ */
+ /* index in root->join_rel_list of this rel */
+ Index join_rel_list_index;
+
+ /*
+ * EquivalenceMembers referencing this rel
+ */
+ List *eclass_child_members;
+
/*
* Information about foreign tables and foreign joins
*/
@@ -1368,6 +1387,10 @@ typedef struct EquivalenceClass
List *ec_opfamilies; /* btree operator family OIDs */
Oid ec_collation; /* collation, if datatypes are collatable */
List *ec_members; /* list of EquivalenceMembers */
+ List *ec_norel_members; /* list of EquivalenceMembers whose
+ * em_relids is empty */
+ List *ec_rel_members; /* list of EquivalenceMembers whose
+ * em_relids is not empty */
List *ec_sources; /* list of generating RestrictInfos */
List *ec_derives; /* list of derived RestrictInfos */
Relids ec_relids; /* all relids appearing in ec_members, except
@@ -1422,6 +1445,9 @@ typedef struct EquivalenceMember
bool em_is_child; /* derived version for a child relation? */
Oid em_datatype; /* the "nominal type" used by the opfamily */
JoinDomain *em_jdomain; /* join domain containing the source clause */
+ Relids em_child_relids; /* all relids of child rels */
+ Bitmapset *em_child_joinrel_relids; /* indexes in root->join_rel_list of
+ join rel children */
/* if em_is_child is true, this links to corresponding EM for top parent */
struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
} EquivalenceMember;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index c43d97b48a..e61f2e3529 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -324,6 +324,24 @@ extern Relids min_join_parameterization(PlannerInfo *root,
extern RelOptInfo *fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind,
Relids relids);
extern Relids find_childrel_parents(PlannerInfo *root, RelOptInfo *rel);
+
+/*
+ * find_relids_top_parents
+ * Compute the set of top-parent relids of rel.
+ *
+ * Replaces all Relids appearing in the given 'relids' as their top-level
+ * parents. The result will be NULL if and only if all of the given relids are
+ * top-level.
+ *
+ * The motivation for having this feature as a macro rather than a function is
+ * that Relids are top-level in most cases. We can quickly determine when
+ * root->top_parent_relid_array is NULL.
+ */
+#define find_relids_top_parents(root, relids) \
+ (likely((root)->top_parent_relid_array == NULL) \
+ ? NULL : find_relids_top_parents_slow(root, relids))
+extern Relids find_relids_top_parents_slow(PlannerInfo *root, Relids relids);
+
extern ParamPathInfo *get_baserel_parampathinfo(PlannerInfo *root,
RelOptInfo *baserel,
Relids required_outer);
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index efd4abc28f..9bc0c36b93 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
Index sortref,
Relids rel,
bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+ EquivalenceClass *ec,
Expr *expr,
Relids relids);
extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -173,6 +174,12 @@ extern void add_child_join_rel_equivalences(PlannerInfo *root,
AppendRelInfo **appinfos,
RelOptInfo *parent_joinrel,
RelOptInfo *child_joinrel);
+extern void add_child_rel_equivalences_to_list(PlannerInfo *root,
+ EquivalenceClass *ec,
+ EquivalenceMember *parent_em,
+ Relids child_relids,
+ List **list,
+ bool *modified);
extern List *generate_implied_equalities_for_column(PlannerInfo *root,
RelOptInfo *rel,
ec_matches_callback_type callback,
--
2.42.0.windows.2
[application/octet-stream] v23-0002-Introduce-indexes-for-RestrictInfo.patch (31.6K, ../CAJ2pMkaaY4FgGSO2z9X_XOoqtLMUhyx6cPZFQbxmsjLgLpEd2Q@mail.gmail.com/4-v23-0002-Introduce-indexes-for-RestrictInfo.patch)
download | inline diff:
From f4f0c7ade55cc5f0d3fc2bc62934710b8e12fa8d Mon Sep 17 00:00:00 2001
From: Yuya Watari <[email protected]>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v23 2/6] Introduce indexes for RestrictInfo
This change was picked up from v19.
Author: David Rowley <[email protected]> and me
---
src/backend/nodes/outfuncs.c | 6 +-
src/backend/nodes/readfuncs.c | 2 +
src/backend/optimizer/path/costsize.c | 3 +-
src/backend/optimizer/path/equivclass.c | 338 +++++++++++++++++++---
src/backend/optimizer/plan/analyzejoins.c | 8 +-
src/backend/optimizer/plan/planner.c | 2 +
src/backend/optimizer/prep/prepjointree.c | 2 +
src/backend/optimizer/util/inherit.c | 7 +
src/include/nodes/parsenodes.h | 6 +
src/include/nodes/pathnodes.h | 47 ++-
src/include/optimizer/paths.h | 15 +-
11 files changed, 383 insertions(+), 53 deletions(-)
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 97ab5d3770..98f824ac53 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,8 +465,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
WRITE_NODE_FIELD(ec_members);
WRITE_NODE_FIELD(ec_norel_members);
WRITE_NODE_FIELD(ec_rel_members);
- WRITE_NODE_FIELD(ec_sources);
- WRITE_NODE_FIELD(ec_derives);
+ WRITE_BITMAPSET_FIELD(ec_source_indexes);
+ WRITE_BITMAPSET_FIELD(ec_derive_indexes);
WRITE_BITMAPSET_FIELD(ec_relids);
WRITE_BOOL_FIELD(ec_has_const);
WRITE_BOOL_FIELD(ec_has_volatile);
@@ -569,6 +569,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
WRITE_BOOL_FIELD(inh);
WRITE_BOOL_FIELD(inFromCl);
WRITE_NODE_FIELD(securityQuals);
+ WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+ WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
}
static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 1624b34581..deebfaf994 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -574,6 +574,8 @@ _readRangeTblEntry(void)
READ_BOOL_FIELD(inh);
READ_BOOL_FIELD(inFromCl);
READ_NODE_FIELD(securityQuals);
+ READ_BITMAPSET_FIELD(eclass_source_indexes);
+ READ_BITMAPSET_FIELD(eclass_derive_indexes);
READ_DONE();
}
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 8b76e98529..ae64b169f8 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5784,7 +5784,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
if (ec && ec->ec_has_const)
{
EquivalenceMember *em = fkinfo->fk_eclass_member[i];
- RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+ RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+ ec,
em);
if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index d9eb4610ce..d1019315ea 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
Expr *expr, Relids relids,
JoinDomain *jdomain,
Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+ RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+ RestrictInfo *rinfo);
static bool is_exprlist_member(Expr *node, List *exprs);
static void generate_base_implied_equalities_const(PlannerInfo *root,
EquivalenceClass *ec);
@@ -321,7 +325,6 @@ process_equivalence(PlannerInfo *root,
/* If case 1, nothing to do, except add to sources */
if (ec1 == ec2)
{
- ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
ec1->ec_min_security = Min(ec1->ec_min_security,
restrictinfo->security_level);
ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -332,6 +335,8 @@ process_equivalence(PlannerInfo *root,
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
+
+ add_eq_source(root, ec1, restrictinfo);
return true;
}
@@ -356,8 +361,10 @@ process_equivalence(PlannerInfo *root,
ec2->ec_norel_members);
ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
ec2->ec_rel_members);
- ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
- ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+ ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+ ec2->ec_source_indexes);
+ ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+ ec2->ec_derive_indexes);
ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
ec1->ec_has_const |= ec2->ec_has_const;
/* can't need to set has_volatile */
@@ -371,10 +378,9 @@ process_equivalence(PlannerInfo *root,
ec2->ec_members = NIL;
ec2->ec_norel_members = NIL;
ec2->ec_rel_members = NIL;
- ec2->ec_sources = NIL;
- ec2->ec_derives = NIL;
+ ec2->ec_source_indexes = NULL;
+ ec2->ec_derive_indexes = NULL;
ec2->ec_relids = NULL;
- ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
ec1->ec_min_security = Min(ec1->ec_min_security,
restrictinfo->security_level);
ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -385,13 +391,14 @@ process_equivalence(PlannerInfo *root,
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
+
+ add_eq_source(root, ec1, restrictinfo);
}
else if (ec1)
{
/* Case 3: add item2 to ec1 */
em2 = add_eq_member(ec1, item2, item2_relids,
jdomain, item2_type);
- ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
ec1->ec_min_security = Min(ec1->ec_min_security,
restrictinfo->security_level);
ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -402,13 +409,14 @@ process_equivalence(PlannerInfo *root,
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
+
+ add_eq_source(root, ec1, restrictinfo);
}
else if (ec2)
{
/* Case 3: add item1 to ec2 */
em1 = add_eq_member(ec2, item1, item1_relids,
jdomain, item1_type);
- ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
ec2->ec_min_security = Min(ec2->ec_min_security,
restrictinfo->security_level);
ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -419,6 +427,8 @@ process_equivalence(PlannerInfo *root,
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
+
+ add_eq_source(root, ec2, restrictinfo);
}
else
{
@@ -430,8 +440,8 @@ process_equivalence(PlannerInfo *root,
ec->ec_members = NIL;
ec->ec_norel_members = NIL;
ec->ec_rel_members = NIL;
- ec->ec_sources = list_make1(restrictinfo);
- ec->ec_derives = NIL;
+ ec->ec_source_indexes = NULL;
+ ec->ec_derive_indexes = NULL;
ec->ec_relids = NULL;
ec->ec_has_const = false;
ec->ec_has_volatile = false;
@@ -453,6 +463,8 @@ process_equivalence(PlannerInfo *root,
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
+
+ add_eq_source(root, ec, restrictinfo);
}
return true;
@@ -600,6 +612,50 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
return em;
}
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+ int source_idx = list_length(root->eq_sources);
+ int i;
+
+ ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+ root->eq_sources = lappend(root->eq_sources, rinfo);
+
+ i = -1;
+ while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[i];
+
+ rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+ source_idx);
+ }
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+ int derive_idx = list_length(root->eq_derives);
+ int i;
+
+ ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+ root->eq_derives = lappend(root->eq_derives, rinfo);
+
+ i = -1;
+ while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[i];
+
+ rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+ derive_idx);
+ }
+}
+
/*
* get_eclass_for_sort_expr
@@ -756,8 +812,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
newec->ec_members = NIL;
newec->ec_norel_members = NIL;
newec->ec_rel_members = NIL;
- newec->ec_sources = NIL;
- newec->ec_derives = NIL;
+ newec->ec_source_indexes = NULL;
+ newec->ec_derive_indexes = NULL;
newec->ec_relids = NULL;
newec->ec_has_const = false;
newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1145,7 +1201,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
* scanning of the quals and before Path construction begins.
*
* We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches. It doesn't really
+ * don't search eq_sources or eq_derives for matches. It doesn't really
* seem worth the trouble to do so.
*/
void
@@ -1234,6 +1290,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
{
EquivalenceMember *const_em = NULL;
ListCell *lc;
+ int i;
/*
* In the trivial case where we just had one "var = const" clause, push
@@ -1243,9 +1300,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
* equivalent to the old one.
*/
if (list_length(ec->ec_members) == 2 &&
- list_length(ec->ec_sources) == 1)
+ bms_get_singleton_member(ec->ec_source_indexes, &i))
{
- RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+ RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
distribute_restrictinfo_to_rels(root, restrictinfo);
return;
@@ -1303,9 +1360,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
/*
* If the clause didn't degenerate to a constant, fill in the correct
- * markings for a mergejoinable clause, and save it in ec_derives. (We
+ * markings for a mergejoinable clause, and save it in eq_derives. (We
* will not re-use such clauses directly, but selectivity estimation
- * may consult the list later. Note that this use of ec_derives does
+ * may consult the list later. Note that this use of eq_derives does
* not overlap with its use for join clauses, since we never generate
* join clauses from an ec_has_const eclass.)
*/
@@ -1315,7 +1372,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
rinfo->left_ec = rinfo->right_ec = ec;
rinfo->left_em = cur_em;
rinfo->right_em = const_em;
- ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+ add_eq_derive(root, ec, rinfo);
}
}
}
@@ -1381,7 +1439,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
/*
* If the clause didn't degenerate to a constant, fill in the
* correct markings for a mergejoinable clause. We don't put it
- * in ec_derives however; we don't currently need to re-find such
+ * in eq_derives however; we don't currently need to re-find such
* clauses, and we don't want to clutter that list with non-join
* clauses.
*/
@@ -1437,11 +1495,12 @@ static void
generate_base_implied_equalities_broken(PlannerInfo *root,
EquivalenceClass *ec)
{
- ListCell *lc;
+ int i = -1;
- foreach(lc, ec->ec_sources)
+ while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
{
- RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+ RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+ root->eq_sources, i);
if (ec->ec_has_const ||
bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1482,11 +1541,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
* Because the same join clauses are likely to be needed multiple times as
* we consider different join paths, we avoid generating multiple copies:
* whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives). This saves memory and allows
- * re-use of information cached in RestrictInfos. We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives). This saves
+ * memory and allows re-use of information cached in RestrictInfos. We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
*
* If we are considering an outer join, sjinfo is the associated OJ info,
* otherwise it can be NULL.
@@ -1871,12 +1930,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
Relids nominal_inner_relids,
RelOptInfo *inner_rel)
{
+ Bitmapset *matching_es;
List *result = NIL;
- ListCell *lc;
+ int i;
- foreach(lc, ec->ec_sources)
+ matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+ i = -1;
+ while ((i = bms_next_member(matching_es, i)) >= 0)
{
- RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+ RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+ root->eq_sources, i);
Relids clause_relids = restrictinfo->required_relids;
if (bms_is_subset(clause_relids, nominal_join_relids) &&
@@ -1888,12 +1951,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
/*
* If we have to translate, just brute-force apply adjust_appendrel_attrs
* to all the RestrictInfos at once. This will result in returning
- * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+ * RestrictInfos that are not listed in eq_derives, but there shouldn't be
* any duplication, and it's a sufficiently narrow corner case that we
* shouldn't sweat too much over it anyway.
*
* Since inner_rel might be an indirect descendant of the baserel
- * mentioned in the ec_sources clauses, we have to be prepared to apply
+ * mentioned in the eq_sources clauses, we have to be prepared to apply
* multiple levels of Var translation.
*/
if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1955,10 +2018,11 @@ create_join_clause(PlannerInfo *root,
EquivalenceMember *rightem,
EquivalenceClass *parent_ec)
{
+ Bitmapset *matches;
RestrictInfo *rinfo;
RestrictInfo *parent_rinfo = NULL;
- ListCell *lc;
MemoryContext oldcontext;
+ int i;
/*
* Search to see if we already built a RestrictInfo for this pair of
@@ -1969,9 +2033,12 @@ create_join_clause(PlannerInfo *root,
* it's not identical, it'd better have the same effects, or the operator
* families we're using are broken.
*/
- foreach(lc, ec->ec_sources)
+ matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+ get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+ i = -1;
+ while ((i = bms_next_member(matches, i)) >= 0)
{
- rinfo = (RestrictInfo *) lfirst(lc);
+ rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
if (rinfo->left_em == leftem &&
rinfo->right_em == rightem &&
rinfo->parent_ec == parent_ec)
@@ -1982,9 +2049,13 @@ create_join_clause(PlannerInfo *root,
return rinfo;
}
- foreach(lc, ec->ec_derives)
+ matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+ get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+ i = -1;
+ while ((i = bms_next_member(matches, i)) >= 0)
{
- rinfo = (RestrictInfo *) lfirst(lc);
+ rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
if (rinfo->left_em == leftem &&
rinfo->right_em == rightem &&
rinfo->parent_ec == parent_ec)
@@ -2042,7 +2113,7 @@ create_join_clause(PlannerInfo *root,
rinfo->left_em = leftem;
rinfo->right_em = rightem;
/* and save it for possible re-use */
- ec->ec_derives = lappend(ec->ec_derives, rinfo);
+ add_eq_derive(root, ec, rinfo);
MemoryContextSwitchTo(oldcontext);
@@ -2721,16 +2792,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
* Returns NULL if no such clause can be found.
*/
RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
EquivalenceMember *em)
{
- ListCell *lc;
+ int i;
Assert(ec->ec_has_const);
Assert(!em->em_is_const);
- foreach(lc, ec->ec_derives)
+
+ i = -1;
+ while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+ RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+ i);
/*
* generate_base_implied_equalities_const will have put non-const
@@ -3335,7 +3409,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
* surely be true if both of them overlap ec_relids.)
*
* Note we don't test ec_broken; if we did, we'd need a separate code
- * path to look through ec_sources. Checking the membership anyway is
+ * path to look through eq_sources. Checking the membership anyway is
* OK as a possibly-overoptimistic heuristic.
*
* We don't test ec_has_const either, even though a const eclass won't
@@ -3423,7 +3497,7 @@ eclass_useful_for_merging(PlannerInfo *root,
/*
* Note we don't test ec_broken; if we did, we'd need a separate code path
- * to look through ec_sources. Checking the members anyway is OK as a
+ * to look through eq_sources. Checking the members anyway is OK as a
* possibly-overoptimistic heuristic.
*/
@@ -3576,3 +3650,179 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
/* Calculate and return the common EC indexes, recycling the left input. */
return bms_int_members(rel1ecs, rel2ecs);
}
+
+/*
+ * get_ec_source_indexes
+ * Returns a Bitmapset with indexes into root->eq_sources for all
+ * RestrictInfos in 'ec' that have
+ * bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+ Bitmapset *rel_esis = NULL;
+ int i = -1;
+
+ while ((i = bms_next_member(relids, i)) >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[i];
+
+ rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+ }
+
+#ifdef USE_ASSERT_CHECKING
+ /* verify the results look sane */
+ i = -1;
+ while ((i = bms_next_member(rel_esis, i)) >= 0)
+ {
+ RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+ i);
+
+ Assert(bms_overlap(relids, rinfo->clause_relids));
+ }
+#endif
+
+ /* bitwise-AND to leave only the ones for this EquivalenceClass */
+ return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ * Returns a Bitmapset with indexes into root->eq_sources for all
+ * RestrictInfos in 'ec' that have
+ * bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+ Relids relids)
+{
+ Bitmapset *esis = NULL;
+ int i = bms_next_member(relids, -1);
+
+ if (i >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[i];
+
+ /*
+ * bms_intersect to the first relation to try to keep the resulting
+ * Bitmapset as small as possible. This saves having to make a
+ * complete bms_copy() of one of them. One may contain significantly
+ * more words than the other.
+ */
+ esis = bms_intersect(ec->ec_source_indexes,
+ rte->eclass_source_indexes);
+
+ while ((i = bms_next_member(relids, i)) >= 0)
+ {
+ rte = root->simple_rte_array[i];
+ esis = bms_int_members(esis, rte->eclass_source_indexes);
+ }
+ }
+
+#ifdef USE_ASSERT_CHECKING
+ /* verify the results look sane */
+ i = -1;
+ while ((i = bms_next_member(esis, i)) >= 0)
+ {
+ RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+ i);
+
+ Assert(bms_is_subset(relids, rinfo->clause_relids));
+ }
+#endif
+
+ return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ * Returns a Bitmapset with indexes into root->eq_derives for all
+ * RestrictInfos in 'ec' that have
+ * bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+ Bitmapset *rel_edis = NULL;
+ int i = -1;
+
+ while ((i = bms_next_member(relids, i)) >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[i];
+
+ rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+ }
+
+#ifdef USE_ASSERT_CHECKING
+ /* verify the results look sane */
+ i = -1;
+ while ((i = bms_next_member(rel_edis, i)) >= 0)
+ {
+ RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+ i);
+
+ Assert(bms_overlap(relids, rinfo->clause_relids));
+ }
+#endif
+
+ /* bitwise-AND to leave only the ones for this EquivalenceClass */
+ return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ * Returns a Bitmapset with indexes into root->eq_derives for all
+ * RestrictInfos in 'ec' that have
+ * bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+ Relids relids)
+{
+ Bitmapset *edis = NULL;
+ int i = bms_next_member(relids, -1);
+
+ if (i >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[i];
+
+ /*
+ * bms_intersect to the first relation to try to keep the resulting
+ * Bitmapset as small as possible. This saves having to make a
+ * complete bms_copy() of one of them. One may contain significantly
+ * more words than the other.
+ */
+ edis = bms_intersect(ec->ec_derive_indexes,
+ rte->eclass_derive_indexes);
+
+ while ((i = bms_next_member(relids, i)) >= 0)
+ {
+ rte = root->simple_rte_array[i];
+ edis = bms_int_members(edis, rte->eclass_derive_indexes);
+ }
+ }
+
+#ifdef USE_ASSERT_CHECKING
+ /* verify the results look sane */
+ i = -1;
+ while ((i = bms_next_member(edis, i)) >= 0)
+ {
+ RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+ i);
+
+ Assert(bms_is_subset(relids, rinfo->clause_relids));
+ }
+#endif
+
+ return edis;
+}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index f0735f3c41..38328d945a 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -669,6 +669,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
int ojrelid)
{
ListCell *lc;
+ int i;
/* Fix up the EC's overall relids */
ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -702,9 +703,10 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
}
/* Fix up the source clauses, in case we can re-use them later */
- foreach(lc, ec->ec_sources)
+ i = -1;
+ while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+ RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
}
@@ -714,7 +716,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
* drop them. (At this point, any such clauses would be base restriction
* clauses, which we'd not need anymore anyway.)
*/
- ec->ec_derives = NIL;
+ ec->ec_derive_indexes = NULL;
}
/*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 667723b675..9420259e4b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -646,6 +646,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->multiexpr_params = NIL;
root->join_domains = NIL;
root->eq_classes = NIL;
+ root->eq_sources = NIL;
+ root->eq_derives = NIL;
root->ec_merging_done = false;
root->last_rinfo_serial = 0;
root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index aa83dd3636..9fc0b69580 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->multiexpr_params = NIL;
subroot->join_domains = NIL;
subroot->eq_classes = NIL;
+ subroot->eq_sources = NIL;
+ subroot->eq_derives = NIL;
subroot->ec_merging_done = false;
subroot->last_rinfo_serial = 0;
subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3501fbbeed..a65a76ace9 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -483,6 +483,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
*/
childrte = makeNode(RangeTblEntry);
memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+ /*
+ * We do not want to inherit the EquivalenceMember indexes of the parent
+ * to its child
+ */
+ childrte->eclass_source_indexes = NULL;
+ childrte->eclass_derive_indexes = NULL;
+
Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
childrte->relid = childOID;
childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b3181f34ae..d365bb5c72 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1194,6 +1194,12 @@ typedef struct RangeTblEntry
bool inh; /* inheritance requested? */
bool inFromCl; /* present in FROM clause? */
List *securityQuals; /* security barrier quals to apply, if any */
+ Bitmapset *eclass_source_indexes; /* Indexes in PlannerInfo's eq_sources
+ * list for RestrictInfos that mention
+ * this relation */
+ Bitmapset *eclass_derive_indexes; /* Indexes in PlannerInfo's eq_derives
+ * list for RestrictInfos that mention
+ * this relation */
} RangeTblEntry;
/*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 8c55a2bcf0..1c5d7bc0c1 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -318,6 +318,12 @@ struct PlannerInfo
/* list of active EquivalenceClasses */
List *eq_classes;
+ /* list of source RestrictInfos used to build EquivalenceClasses */
+ List *eq_sources;
+
+ /* list of RestrictInfos derived from EquivalenceClasses */
+ List *eq_derives;
+
/* set true once ECs are canonical */
bool ec_merging_done;
@@ -1369,6 +1375,41 @@ typedef struct JoinDomain
* entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
* So we record the SortGroupRef of the originating sort clause.
*
+ * TODO: We should update the following comments.
+ *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass. For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large. To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations. This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation. We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass. We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes. This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers. We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list. Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these. These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
* NB: if ec_merged isn't NULL, this class has been merged into another, and
* should be ignored in favor of using the pointed-to class.
*
@@ -1391,8 +1432,10 @@ typedef struct EquivalenceClass
* em_relids is empty */
List *ec_rel_members; /* list of EquivalenceMembers whose
* em_relids is not empty */
- List *ec_sources; /* list of generating RestrictInfos */
- List *ec_derives; /* list of derived RestrictInfos */
+ Bitmapset *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+ * list of generating RestrictInfos */
+ Bitmapset *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+ * list of derived RestrictInfos */
Relids ec_relids; /* all relids appearing in ec_members, except
* for child members (see below) */
bool ec_has_const; /* any pseudoconstants in ec_members? */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9bc0c36b93..d071f6d0fc 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -163,7 +163,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
ForeignKeyOptInfo *fkinfo,
int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+ EquivalenceClass *ec,
EquivalenceMember *em);
extern void add_child_rel_equivalences(PlannerInfo *root,
AppendRelInfo *appinfo,
@@ -195,6 +196,18 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+ EquivalenceClass *ec,
+ Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+ EquivalenceClass *ec,
+ Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+ EquivalenceClass *ec,
+ Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+ EquivalenceClass *ec,
+ Relids relids);
/*
* pathkeys.c
--
2.42.0.windows.2
[application/octet-stream] v23-0003-Solve-conflict-with-self-join-removal.patch (3.8K, ../CAJ2pMkaaY4FgGSO2z9X_XOoqtLMUhyx6cPZFQbxmsjLgLpEd2Q@mail.gmail.com/5-v23-0003-Solve-conflict-with-self-join-removal.patch)
download | inline diff:
From c7525241e4d3c1adac3328420a61ada5aa799107 Mon Sep 17 00:00:00 2001
From: Yuya Watari <[email protected]>
Date: Mon, 11 Dec 2023 12:19:55 +0900
Subject: [PATCH v23 3/6] Solve conflict with self join removal
Written by Alena Rybakina <[email protected]> [1] with
modifications by me.
[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
src/backend/optimizer/plan/analyzejoins.c | 49 ++++++++++++++++-------
1 file changed, 35 insertions(+), 14 deletions(-)
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 38328d945a..49f3c17057 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1589,10 +1589,12 @@ replace_relid(Relids relids, int oldId, int newId)
* delete them.
*/
static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
{
List *new_members = NIL;
- List *new_sources = NIL;
+ Bitmapset *new_source_indexes = NULL;
+ int i;
+ int j;
ListCell *lc;
ListCell *lc1;
@@ -1634,18 +1636,28 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
list_free(ec->ec_members);
ec->ec_members = new_members;
- list_free(ec->ec_derives);
- ec->ec_derives = NULL;
+ i = -1;
+ while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+ {
+ /*
+ * Can't delete the element because we would need to rebuild all
+ * the eq_derives indexes. But set a nuke to detect potential problems.
+ */
+ list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+ }
+ bms_free(ec->ec_derive_indexes);
+ ec->ec_derive_indexes = NULL;
/* Update EC source expressions */
- foreach(lc, ec->ec_sources)
+ i = -1;
+ while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
{
- RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+ RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
bool is_redundant = false;
if (!bms_is_member(from, rinfo->required_relids))
{
- new_sources = lappend(new_sources, rinfo);
+ new_source_indexes = bms_add_member(new_source_indexes, i);
continue;
}
@@ -1656,9 +1668,10 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
* redundancy with existing ones. We don't have to check for
* redundancy with derived clauses, because we've just deleted them.
*/
- foreach(lc1, new_sources)
+ j = -1;
+ while ((j = bms_next_member(new_source_indexes, j)) >= 0)
{
- RestrictInfo *other = lfirst_node(RestrictInfo, lc1);
+ RestrictInfo *other = list_nth_node(RestrictInfo, root->eq_sources, j);
if (!equal(rinfo->clause_relids, other->clause_relids))
continue;
@@ -1670,12 +1683,20 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
}
}
- if (!is_redundant)
- new_sources = lappend(new_sources, rinfo);
+ if (is_redundant)
+ {
+ /*
+ * Can't delete the element because we would need to rebuild all
+ * the eq_sources indexes. But set a nuke to detect potential problems.
+ */
+ list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+ }
+ else
+ new_source_indexes = bms_add_member(new_source_indexes, i);
}
- list_free(ec->ec_sources);
- ec->ec_sources = new_sources;
+ bms_free(ec->ec_source_indexes);
+ ec->ec_source_indexes = new_source_indexes;
ec->ec_relids = replace_relid(ec->ec_relids, from, to);
}
@@ -1855,7 +1876,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
{
EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
- update_eclasses(ec, toRemove->relid, toKeep->relid);
+ update_eclasses(root, ec, toRemove->relid, toKeep->relid);
toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
}
--
2.42.0.windows.2
[application/octet-stream] v23-0004-Fix-a-bug-related-to-atomic-function.patch (3.0K, ../CAJ2pMkaaY4FgGSO2z9X_XOoqtLMUhyx6cPZFQbxmsjLgLpEd2Q@mail.gmail.com/6-v23-0004-Fix-a-bug-related-to-atomic-function.patch)
download | inline diff:
From 914045526469d8010becaa4e3ebebff23167681a Mon Sep 17 00:00:00 2001
From: Yuya Watari <[email protected]>
Date: Mon, 11 Dec 2023 12:20:20 +0900
Subject: [PATCH v23 4/6] Fix a bug related to atomic function
Author: Alena Rybakina <[email protected]>
https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
src/backend/nodes/read.c | 20 ++++++++++++++++++++
src/backend/nodes/readfuncs.c | 24 ++++++++++++++++++++++--
src/include/nodes/readfuncs.h | 2 ++
3 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 969d0ec199..39c5a39fa7 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -514,3 +514,23 @@ nodeRead(const char *token, int tok_len)
return (void *) result;
}
+
+/*
+ * pg_strtok_save_context -
+ * Save context initialized by stringToNode.
+ */
+void
+pg_strtok_save_context(const char **pcontext)
+{
+ *pcontext = pg_strtok_ptr;
+}
+
+/*
+ * pg_strtok_restore_context -
+ * Resore saved context.
+ */
+void
+pg_strtok_restore_context(const char *context)
+{
+ pg_strtok_ptr = context;
+}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index deebfaf994..0b713838dd 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -191,6 +191,26 @@ nullable_string(const char *token, int length)
return debackslash(token, length);
}
+/* Read an equivalence field (anything written as ":fldname %u") and check it */
+#define READ_EQ_BITMAPSET_FIELD_CHECK(fldname) \
+{ \
+ int save_length = length; \
+ const char *context; \
+ pg_strtok_save_context(&context); \
+ token = pg_strtok(&length); \
+ if (length > 0 && strncmp(token, ":"#fldname, strlen(":"#fldname))) \
+ { \
+ /* "fldname" field was not found - fill it and restore context. */ \
+ local_node->fldname = NULL; \
+ pg_strtok_restore_context(context); \
+ length = save_length; \
+ } \
+ else \
+ { \
+ local_node->fldname = _readBitmapset(); \
+ } \
+}
+
/*
* _readBitmapset
@@ -574,8 +594,8 @@ _readRangeTblEntry(void)
READ_BOOL_FIELD(inh);
READ_BOOL_FIELD(inFromCl);
READ_NODE_FIELD(securityQuals);
- READ_BITMAPSET_FIELD(eclass_source_indexes);
- READ_BITMAPSET_FIELD(eclass_derive_indexes);
+ READ_EQ_BITMAPSET_FIELD_CHECK(eclass_source_indexes);
+ READ_EQ_BITMAPSET_FIELD_CHECK(eclass_derive_indexes);
READ_DONE();
}
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index 8466038ed0..0dc2ed5112 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -29,6 +29,8 @@ extern PGDLLIMPORT bool restore_location_fields;
extern const char *pg_strtok(int *length);
extern char *debackslash(const char *token, int length);
extern void *nodeRead(const char *token, int tok_len);
+extern void pg_strtok_save_context(const char **pcontext);
+extern void pg_strtok_restore_context(const char *context);
/*
* prototypes for functions in readfuncs.c
--
2.42.0.windows.2
[application/octet-stream] v23-0005-Remove-all-references-between-RestrictInfo-and-i.patch (2.6K, ../CAJ2pMkaaY4FgGSO2z9X_XOoqtLMUhyx6cPZFQbxmsjLgLpEd2Q@mail.gmail.com/7-v23-0005-Remove-all-references-between-RestrictInfo-and-i.patch)
download | inline diff:
From 0c61c44e693e12ca918bdd24ab266423b0758395 Mon Sep 17 00:00:00 2001
From: Yuya Watari <[email protected]>
Date: Thu, 11 Jan 2024 16:18:36 +0900
Subject: [PATCH v23 5/6] Remove all references between RestrictInfo and its
relating RangeTblEntry
This commit adds code to adjust eclass_derive_indexes and
eclass_source_indexes during self-join elimination.
Note that this commit fails some regression tests. The failures will be
fixed in the next commit. See its commit message for more details.
---
src/backend/optimizer/plan/analyzejoins.c | 34 +++++++++++++++++++++--
1 file changed, 32 insertions(+), 2 deletions(-)
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 49f3c17057..4081827e3a 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1639,9 +1639,25 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
i = -1;
while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
{
+ RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+ /*
+ * Remove all references between this RestrictInfo and its relating
+ * RangeTblEntry.
+ */
+ j = -1;
+ while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[j];
+
+ Assert(bms_is_member(i, rte->eclass_derive_indexes));
+ rte->eclass_derive_indexes =
+ bms_del_member(rte->eclass_derive_indexes, i);
+ }
+
/*
* Can't delete the element because we would need to rebuild all
- * the eq_derives indexes. But set a nuke to detect potential problems.
+ * the eq_derives indexes. But set a null to detect potential problems.
*/
list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
}
@@ -1685,9 +1701,23 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
if (is_redundant)
{
+ /*
+ * Remove all references between this RestrictInfo and its relating
+ * RangeTblEntry.
+ */
+ j = -1;
+ while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[j];
+
+ Assert(bms_is_member(i, rte->eclass_source_indexes));
+ rte->eclass_source_indexes =
+ bms_del_member(rte->eclass_source_indexes, i);
+ }
+
/*
* Can't delete the element because we would need to rebuild all
- * the eq_sources indexes. But set a nuke to detect potential problems.
+ * the eq_sources indexes. But set a null to detect potential problems.
*/
list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
}
--
2.42.0.windows.2
[application/octet-stream] v23-0006-Introduce-update_clause_relids-for-updating-Rest.patch (19.0K, ../CAJ2pMkaaY4FgGSO2z9X_XOoqtLMUhyx6cPZFQbxmsjLgLpEd2Q@mail.gmail.com/8-v23-0006-Introduce-update_clause_relids-for-updating-Rest.patch)
download | inline diff:
From 6bcce92df6dc597e050b7161e5555f3bfd7da841 Mon Sep 17 00:00:00 2001
From: Yuya Watari <[email protected]>
Date: Thu, 11 Jan 2024 16:18:38 +0900
Subject: [PATCH v23 6/6] Introduce update_clause_relids() for updating
RestrictInfo->clause_relids
Originally, the update for RestrictInfo->clause_relids was done by
directly setting the field. However, this failed to update the
corresponding indexes, such as eclass_source_indexes and
eclass_derive_indexes.
This commit provides a helper function to update the field with fixing
the indexes.
---
src/backend/optimizer/path/equivclass.c | 120 ++++++++++++++++++++++
src/backend/optimizer/plan/analyzejoins.c | 81 +++++++++------
src/backend/optimizer/util/appendinfo.c | 2 +
src/backend/optimizer/util/restrictinfo.c | 5 +
src/include/nodes/pathnodes.h | 11 +-
src/include/optimizer/paths.h | 2 +
6 files changed, 191 insertions(+), 30 deletions(-)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index d1019315ea..3f4df1a8c7 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -623,6 +623,8 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
root->eq_sources = lappend(root->eq_sources, rinfo);
+ Assert(rinfo->eq_sources_index == -1);
+ rinfo->eq_sources_index = source_idx;
i = -1;
while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
@@ -645,6 +647,8 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
root->eq_derives = lappend(root->eq_derives, rinfo);
+ Assert(rinfo->eq_derives_index == -1);
+ rinfo->eq_derives_index = derive_idx;
i = -1;
while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
@@ -656,6 +660,122 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
}
}
+/*
+ * update_clause_relids
+ * Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ * The clause_relids field must be updated through this function, not by
+ * setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+ Relids new_clause_relids)
+{
+ int i;
+ Relids removing_relids;
+ Relids adding_relids;
+#ifdef USE_ASSERT_CHECKING
+ Relids common_relids;
+#endif
+
+ /*
+ * If there is nothing to do, we return immediately after setting the
+ * clause_relids field.
+ */
+ if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+ {
+ rinfo->clause_relids = new_clause_relids;
+ return;
+ }
+
+ /*
+ * Remove any references between this RestrictInfo and RangeTblEntries
+ * that are no longer relevant.
+ */
+ removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+ i = -1;
+ while ((i = bms_next_member(removing_relids, i)) >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[i];
+
+ if (rinfo->eq_sources_index != -1)
+ {
+ Assert(bms_is_member(rinfo->eq_sources_index,
+ rte->eclass_source_indexes));
+ rte->eclass_source_indexes =
+ bms_del_member(rte->eclass_source_indexes,
+ rinfo->eq_sources_index);
+ }
+ if (rinfo->eq_derives_index != -1)
+ {
+ Assert(bms_is_member(rinfo->eq_derives_index,
+ rte->eclass_derive_indexes));
+ rte->eclass_derive_indexes =
+ bms_del_member(rte->eclass_derive_indexes,
+ rinfo->eq_derives_index);
+ }
+ }
+ bms_free(removing_relids);
+
+ /*
+ * Add references between this RestrictInfo and RangeTblEntries that will
+ * be relevant.
+ */
+ adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+ i = -1;
+ while ((i = bms_next_member(adding_relids, i)) >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[i];
+
+ if (rinfo->eq_sources_index != -1)
+ {
+ Assert(!bms_is_member(rinfo->eq_sources_index,
+ rte->eclass_source_indexes));
+ rte->eclass_source_indexes =
+ bms_add_member(rte->eclass_source_indexes,
+ rinfo->eq_sources_index);
+ }
+ if (rinfo->eq_derives_index != -1)
+ {
+ Assert(!bms_is_member(rinfo->eq_derives_index,
+ rte->eclass_derive_indexes));
+ rte->eclass_derive_indexes =
+ bms_add_member(rte->eclass_derive_indexes,
+ rinfo->eq_derives_index);
+ }
+ }
+ bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+ /*
+ * Verify that all indexes are set for common Relids.
+ */
+ common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+ i = -1;
+ while ((i = bms_next_member(common_relids, i)) >= 0)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[i];
+
+ if (rinfo->eq_sources_index != -1)
+ {
+ Assert(bms_is_member(rinfo->eq_sources_index,
+ rte->eclass_source_indexes));
+ }
+ if (rinfo->eq_derives_index != -1)
+ {
+ Assert(bms_is_member(rinfo->eq_derives_index,
+ rte->eclass_derive_indexes));
+ }
+ }
+ bms_free(common_relids);
+#endif
+
+ /*
+ * Since we have done everything to adjust the indexes, we can set the
+ * clause_relids field.
+ */
+ rinfo->clause_relids = new_clause_relids;
+}
+
/*
* get_eclass_for_sort_expr
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 4081827e3a..2da2a6c14e 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -39,6 +39,7 @@
*/
typedef struct
{
+ PlannerInfo *root;
int from;
int to;
} ReplaceVarnoContext;
@@ -59,7 +60,8 @@ static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+ RestrictInfo *rinfo,
int relid, int ojrelid);
static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
int relid, int ojrelid);
@@ -76,7 +78,7 @@ static bool is_innerrel_unique_for(PlannerInfo *root,
List *restrictlist,
List **extra_clauses);
static Bitmapset *replace_relid(Relids relids, int oldId, int newId);
-static void replace_varno(Node *node, int from, int to);
+static void replace_varno(PlannerInfo *root, Node *node, int from, int to);
static bool replace_varno_walker(Node *node, ReplaceVarnoContext *ctx);
static int self_join_candidates_cmp(const void *a, const void *b);
@@ -395,7 +397,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
}
/* Update lateral references. */
- replace_varno((Node *) otherrel->lateral_vars, relid, subst);
+ replace_varno(root, (Node *) otherrel->lateral_vars, relid, subst);
}
/*
@@ -432,7 +434,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
sjinf->commute_below_l = replace_relid(sjinf->commute_below_l, ojrelid, subst);
sjinf->commute_below_r = replace_relid(sjinf->commute_below_r, ojrelid, subst);
- replace_varno((Node *) sjinf->semi_rhs_exprs, relid, subst);
+ replace_varno(root, (Node *) sjinf->semi_rhs_exprs, relid, subst);
}
/*
@@ -477,7 +479,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
phv->phrels = replace_relid(phv->phrels, relid, subst);
phv->phrels = replace_relid(phv->phrels, ojrelid, subst);
Assert(!bms_is_empty(phv->phrels));
- replace_varno((Node *) phv->phexpr, relid, subst);
+ replace_varno(root, (Node *) phv->phexpr, relid, subst);
Assert(phv->phnullingrels == NULL); /* no need to adjust */
}
}
@@ -551,7 +553,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
* that any such PHV is safe (and updated its ph_eval_at), so we
* can just drop those references.
*/
- remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+ remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
/*
* Cross-check that the clause itself does not reference the
@@ -608,16 +610,24 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
* we have to also clean up the sub-clauses.
*/
static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+ int ojrelid)
{
+ Relids new_clause_relids;
+
+ /*
+ * The 'new_clause_relids' passed to update_clause_relids() must be a
+ * different instance from the current rinfo->clause_relids, so we make a
+ * copy of them.
+ */
+ new_clause_relids = bms_copy(rinfo->clause_relids);
+ new_clause_relids = bms_del_member(new_clause_relids, relid);
+ new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+ update_clause_relids(root, rinfo, new_clause_relids);
/*
- * The clause_relids probably aren't shared with anything else, but let's
+ * The required_relids probably aren't shared with anything else, but let's
* copy them just to be sure.
*/
- rinfo->clause_relids = bms_copy(rinfo->clause_relids);
- rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
- rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
- /* Likewise for required_relids */
rinfo->required_relids = bms_copy(rinfo->required_relids);
rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -642,14 +652,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
{
RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
- remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+ remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
}
}
else
{
RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
- remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+ remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
}
}
}
@@ -708,7 +718,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
{
RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
- remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+ remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
}
/*
@@ -1448,13 +1458,14 @@ is_innerrel_unique_for(PlannerInfo *root,
* Replace each occurrence of removing relid with the keeping one
*/
static void
-replace_varno(Node *node, int from, int to)
+replace_varno(PlannerInfo *root, Node *node, int from, int to)
{
ReplaceVarnoContext ctx;
if (to <= 0)
return;
+ ctx.root = root;
ctx.from = from;
ctx.to = to;
replace_varno_walker(node, &ctx);
@@ -1498,9 +1509,9 @@ replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
if (bms_is_member(ctx->from, rinfo->clause_relids))
{
- replace_varno((Node *) rinfo->clause, ctx->from, ctx->to);
- replace_varno((Node *) rinfo->orclause, ctx->from, ctx->to);
- rinfo->clause_relids = replace_relid(rinfo->clause_relids, ctx->from, ctx->to);
+ replace_varno(ctx->root, (Node *) rinfo->clause, ctx->from, ctx->to);
+ replace_varno(ctx->root, (Node *) rinfo->orclause, ctx->from, ctx->to);
+ update_clause_relids(ctx->root, rinfo, replace_relid(rinfo->clause_relids, ctx->from, ctx->to));
rinfo->left_relids = replace_relid(rinfo->left_relids, ctx->from, ctx->to);
rinfo->right_relids = replace_relid(rinfo->right_relids, ctx->from, ctx->to);
}
@@ -1613,7 +1624,7 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
em->em_jdomain->jd_relids = replace_relid(em->em_jdomain->jd_relids, from, to);
/* We only process inner joins */
- replace_varno((Node *) em->em_expr, from, to);
+ replace_varno(root, (Node *) em->em_expr, from, to);
foreach(lc1, new_members)
{
@@ -1660,6 +1671,12 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
* the eq_derives indexes. But set a null to detect potential problems.
*/
list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+
+ /*
+ * Since this RestrictInfo no longer exists in root->eq_derives, we
+ * must reset the stored index.
+ */
+ rinfo->eq_derives_index = -1;
}
bms_free(ec->ec_derive_indexes);
ec->ec_derive_indexes = NULL;
@@ -1677,7 +1694,7 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
continue;
}
- replace_varno((Node *) rinfo, from, to);
+ replace_varno(root, (Node *) rinfo, from, to);
/*
* After switching the clause to the remaining relation, check it for
@@ -1720,6 +1737,12 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
* the eq_sources indexes. But set a null to detect potential problems.
*/
list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+
+ /*
+ * Since this RestrictInfo no longer exists in root->eq_sources, we
+ * must reset the stored index.
+ */
+ rinfo->eq_sources_index = -1;
}
else
new_source_indexes = bms_add_member(new_source_indexes, i);
@@ -1782,7 +1805,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
int i;
List *jinfo_candidates = NIL;
List *binfo_candidates = NIL;
- ReplaceVarnoContext ctx = {.from = toRemove->relid,.to = toKeep->relid};
+ ReplaceVarnoContext ctx = {.root = root,.from = toRemove->relid,.to = toKeep->relid};
Assert(toKeep->relid != -1);
@@ -1798,7 +1821,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
- replace_varno((Node *) rinfo, toRemove->relid, toKeep->relid);
+ replace_varno(root, (Node *) rinfo, toRemove->relid, toKeep->relid);
if (bms_membership(rinfo->required_relids) == BMS_MULTIPLE)
jinfo_candidates = lappend(jinfo_candidates, rinfo);
@@ -1818,7 +1841,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
{
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
- replace_varno((Node *) rinfo, toRemove->relid, toKeep->relid);
+ replace_varno(root, (Node *) rinfo, toRemove->relid, toKeep->relid);
if (bms_membership(rinfo->required_relids) == BMS_MULTIPLE)
jinfo_candidates = lappend(jinfo_candidates, rinfo);
@@ -1918,7 +1941,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
{
Node *node = lfirst(lc);
- replace_varno(node, toRemove->relid, toKeep->relid);
+ replace_varno(root, node, toRemove->relid, toKeep->relid);
if (!list_member(toKeep->reltarget->exprs, node))
toKeep->reltarget->exprs = lappend(toKeep->reltarget->exprs, node);
}
@@ -1970,9 +1993,9 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
remove_rel_from_query(root, toRemove, toKeep->relid, NULL, NULL);
/* At last, replace varno in root targetlist and HAVING clause */
- replace_varno((Node *) root->processed_tlist,
+ replace_varno(root, (Node *) root->processed_tlist,
toRemove->relid, toKeep->relid);
- replace_varno((Node *) root->processed_groupClause,
+ replace_varno(root, (Node *) root->processed_groupClause,
toRemove->relid, toKeep->relid);
replace_relid(root->all_result_relids, toRemove->relid, toKeep->relid);
replace_relid(root->leaf_result_relids, toRemove->relid, toKeep->relid);
@@ -2049,7 +2072,7 @@ split_selfjoin_quals(PlannerInfo *root, List *joinquals, List **selfjoinquals,
* when we have cast of the same var to different (but compatible)
* types.
*/
- replace_varno(rightexpr, bms_singleton_member(rinfo->right_relids),
+ replace_varno(root, rightexpr, bms_singleton_member(rinfo->right_relids),
bms_singleton_member(rinfo->left_relids));
if (equal(leftexpr, rightexpr))
@@ -2090,7 +2113,7 @@ match_unique_clauses(PlannerInfo *root, RelOptInfo *outer, List *uclauses,
bms_is_empty(rinfo->right_relids));
clause = (Expr *) copyObject(rinfo->clause);
- replace_varno((Node *) clause, relid, outer->relid);
+ replace_varno(root, (Node *) clause, relid, outer->relid);
iclause = bms_is_empty(rinfo->left_relids) ? get_rightop(clause) :
get_leftop(clause);
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 51fdeace7d..27e4b093d7 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -491,6 +491,8 @@ adjust_appendrel_attrs_mutator(Node *node,
newinfo->right_bucketsize = -1;
newinfo->left_mcvfreq = -1;
newinfo->right_mcvfreq = -1;
+ newinfo->eq_sources_index = -1;
+ newinfo->eq_derives_index = -1;
return (Node *) newinfo;
}
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 0b406e9334..59ba503ecb 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -247,6 +247,9 @@ make_restrictinfo_internal(PlannerInfo *root,
restrictinfo->left_hasheqoperator = InvalidOid;
restrictinfo->right_hasheqoperator = InvalidOid;
+ restrictinfo->eq_sources_index = -1;
+ restrictinfo->eq_derives_index = -1;
+
return restrictinfo;
}
@@ -403,6 +406,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
result->right_mcvfreq = rinfo->left_mcvfreq;
result->left_hasheqoperator = InvalidOid;
result->right_hasheqoperator = InvalidOid;
+ result->eq_sources_index = -1;
+ result->eq_derives_index = -1;
return result;
}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 1c5d7bc0c1..b856722533 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -2622,7 +2622,12 @@ typedef struct RestrictInfo
/* number of base rels in clause_relids */
int num_base_rels pg_node_attr(equal_ignore);
- /* The relids (varnos+varnullingrels) actually referenced in the clause: */
+ /*
+ * The relids (varnos+varnullingrels) actually referenced in the clause.
+ *
+ * NOTE: This field must be updated through update_clause_relids(), not by
+ * setting the field directly.
+ */
Relids clause_relids pg_node_attr(equal_ignore);
/* The set of relids required to evaluate the clause: */
@@ -2737,6 +2742,10 @@ typedef struct RestrictInfo
/* hash equality operators used for memoize nodes, else InvalidOid */
Oid left_hasheqoperator pg_node_attr(equal_ignore);
Oid right_hasheqoperator pg_node_attr(equal_ignore);
+
+ /* the index within root->eq_sources and root->eq_derives */
+ int eq_sources_index pg_node_attr(equal_ignore);
+ int eq_derives_index pg_node_attr(equal_ignore);
} RestrictInfo;
/*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d071f6d0fc..c23b177f65 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -128,6 +128,8 @@ extern bool process_equivalence(PlannerInfo *root,
extern Expr *canonicalize_ec_expression(Expr *expr,
Oid req_type, Oid req_collation);
extern void reconsider_outer_join_clauses(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+ Relids new_clause_relids);
extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
Expr *expr,
List *opfamilies,
--
2.42.0.windows.2
view thread (21+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: [PoC] Reducing planning time when tables have many partitions
In-Reply-To: <CAJ2pMkaaY4FgGSO2z9X_XOoqtLMUhyx6cPZFQbxmsjLgLpEd2Q@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox