1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-04 20:11:56 +03:00

Invent "join domains" to replace the below_outer_join hack.

EquivalenceClasses are now understood as applying within a "join
domain", which is a set of inner-joined relations (possibly underneath
an outer join).  We no longer need to treat an EC from below an outer
join as a second-class citizen.

I have hopes of eventually being able to treat outer-join clauses via
EquivalenceClasses, by means of only applying deductions within the
EC's join domain.  There are still problems in the way of that, though,
so for now the reconsider_outer_join_clause logic is still here.

I haven't been able to get rid of RestrictInfo.is_pushed_down either,
but I wonder if that could be recast using JoinDomains.

I had to hack one test case in postgres_fdw.sql to make it still test
what it was meant to, because postgres_fdw is inconsistent about
how it deals with quals containing non-shippable expressions; see
https://postgr.es/m/1691374.1671659838@sss.pgh.pa.us.  That should
be improved, but I don't think it's within the scope of this patch
series.

Patch by me; thanks to Richard Guo for review.

Discussion: https://postgr.es/m/830269.1656693747@sss.pgh.pa.us
This commit is contained in:
Tom Lane
2023-01-30 13:50:25 -05:00
parent b448f1c8d8
commit 3bef56e116
12 changed files with 269 additions and 193 deletions

View File

@@ -307,6 +307,9 @@ struct PlannerInfo
/* List of Lists of Params for MULTIEXPR subquery outputs */
List *multiexpr_params;
/* list of JoinDomains used in the query (higher ones first) */
List *join_domains;
/* list of active EquivalenceClasses */
List *eq_classes;
@@ -1278,11 +1281,46 @@ typedef struct StatisticExtInfo
List *exprs;
} StatisticExtInfo;
/*
* JoinDomains
*
* A "join domain" defines the scope of applicability of deductions made via
* the EquivalenceClass mechanism. Roughly speaking, a join domain is a set
* of base+OJ relations that are inner-joined together. More precisely, it is
* the set of relations at which equalities deduced from an EquivalenceClass
* can be enforced or should be expected to hold. The topmost JoinDomain
* covers the whole query (so its jd_relids should equal all_query_rels).
* An outer join creates a new JoinDomain that includes all base+OJ relids
* within its nullable side, but (by convention) not the OJ's own relid.
* A FULL join creates two new JoinDomains, one for each side.
*
* Notice that a rel that is below outer join(s) will thus appear to belong
* to multiple join domains. However, any of its Vars that appear in
* EquivalenceClasses belonging to higher join domains will have nullingrel
* bits preventing them from being evaluated at the rel's scan level, so that
* we will not be able to derive enforceable-at-the-rel-scan-level clauses
* from such ECs. We define the join domain relid sets this way so that
* domains can be said to be "higher" or "lower" when one domain relid set
* includes another.
*
* The JoinDomains for a query are computed in deconstruct_jointree.
* We do not copy JoinDomain structs once made, so they can be compared
* for equality by simple pointer equality.
*/
typedef struct JoinDomain
{
pg_node_attr(no_copy_equal, no_read)
NodeTag type;
Relids jd_relids; /* all relids contained within the domain */
} JoinDomain;
/*
* EquivalenceClasses
*
* Whenever we can determine that a mergejoinable equality clause A = B is
* not delayed by any outer join, we create an EquivalenceClass containing
* Whenever we identify a mergejoinable equality clause A = B that is
* not an outer-join clause, we create an EquivalenceClass containing
* the expressions A and B to record this knowledge. If we later find another
* equivalence B = C, we add C to the existing EquivalenceClass; this may
* require merging two existing EquivalenceClasses. At the end of the qual
@@ -1296,6 +1334,18 @@ typedef struct StatisticExtInfo
* that all or none of the input datatypes are collatable, so that a single
* collation value is sufficient.)
*
* Strictly speaking, deductions from an EquivalenceClass hold only within
* a "join domain", that is a set of relations that are innerjoined together
* (see JoinDomain above). For the most part we don't need to account for
* this explicitly, because equality clauses from different join domains
* will contain Vars that are not equal() because they have different
* nullingrel sets, and thus we will never falsely merge ECs from different
* join domains. But Var-free (pseudoconstant) expressions lack that safety
* feature. We handle that by marking "const" EC members with the JoinDomain
* of the clause they came from; two nominally-equal const members will be
* considered different if they came from different JoinDomains. This ensures
* no false EquivalenceClass merges will occur.
*
* We also use EquivalenceClasses as the base structure for PathKeys, letting
* us represent knowledge about different sort orderings being equivalent.
* Since every PathKey must reference an EquivalenceClass, we will end up
@@ -1310,11 +1360,6 @@ typedef struct StatisticExtInfo
* entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
* So we record the SortGroupRef of the originating sort clause.
*
* We allow equality clauses appearing below the nullable side of an outer join
* to form EquivalenceClasses, but these have a slightly different meaning:
* the included values might be all NULL rather than all the same non-null
* values. See src/backend/optimizer/README for more on that point.
*
* 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.
*
@@ -1339,7 +1384,6 @@ typedef struct EquivalenceClass
* for child members (see below) */
bool ec_has_const; /* any pseudoconstants in ec_members? */
bool ec_has_volatile; /* the (sole) member is a volatile expr */
bool ec_below_outer_join; /* equivalence applies below an OJ */
bool ec_broken; /* failed to generate needed clauses? */
Index ec_sortref; /* originating sortclause label, or 0 */
Index ec_min_security; /* minimum security_level in ec_sources */
@@ -1348,11 +1392,11 @@ typedef struct EquivalenceClass
} EquivalenceClass;
/*
* If an EC contains a const and isn't below-outer-join, any PathKey depending
* on it must be redundant, since there's only one possible value of the key.
* If an EC contains a constant, any PathKey depending on it must be
* redundant, since there's only one possible value of the key.
*/
#define EC_MUST_BE_REDUNDANT(eclass) \
((eclass)->ec_has_const && !(eclass)->ec_below_outer_join)
((eclass)->ec_has_const)
/*
* EquivalenceMember - one member expression of an EquivalenceClass
@@ -1387,6 +1431,7 @@ typedef struct EquivalenceMember
bool em_is_const; /* expression is pseudoconstant? */
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 */
/* 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;