Re: "failed to build any N-way joins" from a five-relation query - Mailing list pgsql-hackers

From wenhui qiu
Subject Re: "failed to build any N-way joins" from a five-relation query
Date
Msg-id CAGjGUAL=ZbJvtk2h3QNLzxRMHR8vnC=ioWyEB3ZFKcVXqxnKjw@mail.gmail.com
Whole thread
In response to "failed to build any N-way joins" from a five-relation query  (Richard Guo <guofenglinux@gmail.com>)
List pgsql-hackers
Hi Richard Guo 

Thanks for the patch and the detailed analysis.

The problem diagnosis and the example query (t1 --LJ--> t2 <--lateral-- s1 --LJ--> t3 <--lateral-- s2) are very clear. Rejecting joins whose lateral references are trapped inside outer joins or downstream lateral dependencies is topologically sound and nicely fixes the "failed to build any 5-way joins" failure.

Just a couple of small thoughts regarding the implementation and back-patching, for your consideration:

1. Leveraging existing lateral_referencers

In the patch, the search loops over simple_rel_array on every iteration of do ... while (more):


+                   /* add rels that laterally reference any rel found so far */
+                   for (int rti = 1; rti < root->simple_rel_array_size; rti++)
+                   {
+                        RelOptInfo *brel = root->simple_rel_array[rti];
...
+                        if (!bms_is_member(rti, join_plus_rhs) &&
+                             bms_overlap(brel->lateral_relids, join_plus_rhs))
+                        {
+                             join_plus_rhs = bms_add_member(join_plus_rhs, rti);
+                             more = true;
+                        }
+                   }

Since join_is_legal() is on the critical hot path of join search, doing a full linear scan of simple_rel_array and checking bms_overlap() on every iteration could introduce overhead, especially for queries on partitioned tables where simple_rel_array_size can be large.

In src/backend/optimizer/plan/initsplan.c, create_lateral_join_info() already builds the inverse mapping with transitive closure already computed:


/*
 * Now that we've identified all lateral references, mark each baserel
 * with the set of relids of rels that reference it laterally (possibly
 * indirectly) --- that is, the inverse mapping of lateral_relids.
 */
brel2->lateral_referencers = bms_add_member(brel2->lateral_referencers, rti);

Could we take advantage of brel->lateral_referencers for the relations already present in join_plus_rhs instead of scanning all baserels from scratch? That would directly yield all direct and indirect lateral referencers without repeatedly inspecting unrelated baserels.

2. Interactions with PG 17+ Outer Join infrastructure (ojrelid)

The patch targets Backpatch-through: 14. However, in PG 17 and master, the outer join infrastructure was significantly refactored by Tom Lane to give outer joins their own ojrelid (root->outer_join_rels, sjinfo->ojrelid).

Notice the check that immediately follows this loop in v17/master:


if (bms_overlap(join_lateral_rels, root->outer_join_rels))
{
    foreach(l, root->join_info_list)
    {
        SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l);
        if (!bms_is_member(sjinfo->ojrelid, join_lateral_rels))
            continue;
        if (bms_overlap(join_plus_rhs, sjinfo->min_lefthand) ||
            bms_overlap(join_plus_rhs, sjinfo->min_righthand))
            return false;   /* OJ can't be formed outside join */
    }
}

In PG 17 and master, join_plus_rhs can contain ojrelids in addition to baserel relids. The proposed loop skips anything where brel == NULL || brel->reloptkind != RELOPT_BASEREL. While this assumption holds cleanly on v14–v16, on v17/master:

If join_plus_rhs contains an ojrelid, looking only at baserel lateral_relids might miss lateral dependencies that reference an outer join (for instance, via a PlaceHolderVar evaluated at that outer join).
Conversely, whether join_plus_rhs containing ojrelids needs explicit propagation through lateral references should probably be verified or documented to ensure complete branch compatibility between v14–v16 and v17/master.

What are your thoughts on these two points?


Thanks

pgsql-hackers by date:

Previous
From: jian he
Date:
Subject: Re: Row pattern recognition
Next
From: "Chee Wooson"
Date:
Subject: Re: Re: [PATCH] Discard aborted updaters when expanding a multixact