tglsfdc · GitHub

Commits on Aug 22, 2026

  1. Perform join removal by editing the query's jointree.

    analyzejoins.c decided which joins could be dropped by consulting the
    planner's derived data structures, but then implemented the removal
    by updating those structures in-place.  That is a lot of fiddly work,
    and nothing keeps it in step with the rest of the planner:
    remove_leftjoinrel_from_query only bothered to update "parts of the
    planner's data structures that will actually be consulted later", with
    no good way to know what those are.  Bug #19560 is one consequence.
    In that report, removing a join leaves an EquivalenceClass that now
    gives rise to a base restriction clause, but base restriction clauses
    have already been generated and nothing reconsiders them, so the WHERE
    condition disappears from the plan and we return wrong answers.
    The self-join elimination code has the same design and the same type
    of hazard.  We have seen many related bugs over the years too, so it's
    time to do something drastic.
    To fix, do the removals by editing root->parse->jointree (which is a
    far simpler and more stable representation than the derived data),
    and then have query_planner() discard everything it computed from the
    jointree and derive it over again.  This requires quite a bit less
    code, and doesn't require touching analyzejoins.c every time we change
    the data derived by query_planner(), and it doesn't seem to result in
    any significant planning-time penalty.
    reduce_unique_semijoins() gets the same treatment: rather than deleting
    the semijoin's SpecialJoinInfo and relying on the jointree not being
    consulted again, it now changes the JoinExpr's jointype to JOIN_INNER.
    Some plans change in the join regression test.  Qual evaluation order
    shifts in a few cases, because the conditions now reach later planning
    in jointree order rather than in whatever order the removal code
    re-distributed them.  A few plans improve, since the rebuilt relation
    targetlists no longer carry columns that only a removed join needed.
    We also detect a constant-false filter condition whose test used to
    carry a FIXME label.  One plan gets marginally worse, because the old
    code recomputed attr_needed from equivalence classes after a join
    removal; that is more accurate than what deconstruct_jointree()
    derives from the original clauses, but we no longer do that.  Making
    that recomputation happen anyway could be worth doing, but it should
    be considered independently and perhaps implemented differently.
    Full disclosure: initial drafts of this patch were made with
    Claude Opus 4.8.
    Bug: #19560
    Reported-by: Orestis Markou <orestis@orestis.gr>
    Author: Tom Lane <tgl@sss.pgh.pa.us>
    Reviewed-by: Thom Brown <thom@linux.com>
    Reviewed-by: Jacob Brazeal <jacob.brazeal@gmail.com>
    Discussion: https://postgr.es/m/1186816.1784573544@sss.pgh.pa.us
    Backpatch-through: 16

    authored and Commitfest Bot committed

    Aug 22, 2026
    Configuration menu

    Browse the repository at this point in the history

  2. Configuration menu

    Browse the repository at this point in the history

Read the original on github.com ↗