Hi Richard,
thank you for your great work with the optimizer!
I just stumbled and by accident landed on your commit 9a60f295bcb (and
then looked up the discussion
https://www.postgresql.org/message-id/flat/CALzhyqwryL2QywgO03VQr_237Sq3MEVgTTT2_A9G3nGT5-SRZg%40mail.gmail.com).
I am under the impression we need to extend the same fix for or clauses
descending into the orclause sub-RestrictInfos.
This is the simplified minimal test case, that is failing for me:
CREATE TABLE a (id int PRIMARY KEY, b_id int);
CREATE TABLE uniq (id int PRIMARY KEY);
CREATE TABLE parted_b (id int PRIMARY KEY) PARTITION BY RANGE (id);
CREATE TABLE parted_b1 PARTITION OF parted_b FOR VALUES FROM (0) TO (10);
SELECT 1 FROM a, (SELECT t1.id FROM parted_b t1 LEFT JOIN uniq t2 ON
t1.id = t2.id) s
WHERE (s.id = 1 AND a.id = 2) OR (s.id = 3 AND a.id = 4) GROUP BY ();
My idea would be to put a bitmapset into remove_rel_from_query with the
seen ones and recurse removing it from the rinfo. I attached a sketch of
my idea. But maybe our have a better one.
Regards
Arne
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index f109af25d72..a419be11609 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -66,6 +66,8 @@ static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
int relid, int ojrelid);
static Node *remove_rel_from_phvs(Node *node, int relid, int ojrelid);
static Node *remove_rel_from_phvs_mutator(Node *node, Relids removable);
+static void remove_rel_from_phvs_in_rinfo(RestrictInfo *rinfo,
+ 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);
static bool rel_is_distinct_for(PlannerInfo *root, RelOptInfo *rel,
@@ -457,6 +459,7 @@ remove_rel_from_query(PlannerInfo *root, int relid,
ListCell *l;
bool is_outer_join = (sjinfo != NULL);
bool is_self_join = (!is_outer_join && subst > 0);
+ Bitmapset *seen_serials = NULL;
Assert(is_outer_join || is_self_join);
Assert(!is_outer_join || ojrelid > 0);
@@ -676,9 +679,22 @@ remove_rel_from_query(PlannerInfo *root, int relid,
if (is_outer_join && rti != relid && root->glob->lastPHId != 0)
{
foreach_node(RestrictInfo, rinfo, otherrel->baserestrictinfo)
+ remove_rel_from_phvs_in_rinfo(rinfo, relid, ojrelid);
+
+ foreach_node(RestrictInfo, rinfo, otherrel->joininfo)
{
- rinfo->clause = (Expr *)
- remove_rel_from_phvs((Node *) rinfo->clause, relid, ojrelid);
+ /*
+ * This doesn't work for is_clone clauses,
+ * since their serial numbers are not unique.
+ */
+ if (!rinfo->is_clone)
+ {
+ if (bms_is_member(rinfo->rinfo_serial, seen_serials))
+ continue;
+ seen_serials = bms_add_member(seen_serials,
+ rinfo->rinfo_serial);
+ }
+ remove_rel_from_phvs_in_rinfo(rinfo, relid, ojrelid);
}
}
}
@@ -907,6 +923,43 @@ remove_rel_from_phvs_mutator(Node *node, Relids removable)
removable);
}
+static void
+remove_rel_from_phvs_in_rinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+{
+ rinfo->clause = (Expr *)
+ remove_rel_from_phvs((Node *) rinfo->clause, relid, ojrelid);
+
+ if (restriction_is_or_clause(rinfo))
+ {
+ ListCell *lc;
+
+ Assert(is_orclause(rinfo->orclause));
+ foreach(lc, ((BoolExpr *) rinfo->orclause)->args)
+ {
+ Node *orarg = (Node *) lfirst(lc);
+
+ /* OR arguments should be ANDs or sub-RestrictInfos */
+ if (is_andclause(orarg))
+ {
+ ListCell *lc2;
+
+ foreach(lc2, ((BoolExpr *) orarg)->args)
+ {
+ RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
+
+ remove_rel_from_phvs_in_rinfo(rinfo2, relid, ojrelid);
+ }
+ }
+ else
+ {
+ RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
+
+ remove_rel_from_phvs_in_rinfo(rinfo2, relid, ojrelid);
+ }
+ }
+ }
+}
+
/*
* Remove any occurrences of the target relid from a joinlist structure.
*