Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 64ad858

Browse files
committed
Change more places to be less trusting of RestrictInfo.is_pushed_down.
On further reflection, commit e5d8399 didn't go far enough: pretty much everywhere in the planner that examines a clause's is_pushed_down flag ought to be changed to use the more complicated behavior where we also check the clause's required_relids. Otherwise we could make incorrect decisions about whether, say, a clause is safe to use as a hash clause. Some (many?) of these places are safe as-is, either because they are never reached while considering a parameterized path, or because there are additional checks that would reject a pushed-down clause anyway. However, it seems smarter to just code them all the same way rather than rely on easily-broken reasoning of that sort. In support of that, invent a new macro RINFO_IS_PUSHED_DOWN that should be used in place of direct tests on the is_pushed_down flag. Like the previous patch, back-patch to all supported branches. Discussion: https://postgr.es/m/f8128b11-c5bf-3539-48cd-234178b2314d@proxel.se
1 parent 306d6e5 commit 64ad858

File tree

7 files changed

+52
-32
lines changed

7 files changed

+52
-32
lines changed

src/backend/optimizer/path/costsize.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ static bool has_indexed_join_quals(NestPath *joinpath);
147147
static double approx_tuple_count(PlannerInfo *root, JoinPath *path,
148148
List *quals);
149149
static double calc_joinrel_size_estimate(PlannerInfo *root,
150+
RelOptInfo *joinrel,
150151
RelOptInfo *outer_rel,
151152
RelOptInfo *inner_rel,
152153
double outer_rows,
@@ -3542,13 +3543,15 @@ compute_semi_anti_join_factors(PlannerInfo *root,
35423543
*/
35433544
if (jointype == JOIN_ANTI)
35443545
{
3546+
Relids joinrelids = bms_union(outerrel->relids, innerrel->relids);
3547+
35453548
joinquals = NIL;
35463549
foreach(l, restrictlist)
35473550
{
35483551
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
35493552

35503553
Assert(IsA(rinfo, RestrictInfo));
3551-
if (!rinfo->is_pushed_down)
3554+
if (!RINFO_IS_PUSHED_DOWN(rinfo, joinrelids))
35523555
joinquals = lappend(joinquals, rinfo);
35533556
}
35543557
}
@@ -3863,6 +3866,7 @@ set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
38633866
List *restrictlist)
38643867
{
38653868
rel->rows = calc_joinrel_size_estimate(root,
3869+
rel,
38663870
outer_rel,
38673871
inner_rel,
38683872
outer_rel->rows,
@@ -3905,6 +3909,7 @@ get_parameterized_joinrel_size(PlannerInfo *root, RelOptInfo *rel,
39053909
* estimate for any pair with the same parameterization.
39063910
*/
39073911
nrows = calc_joinrel_size_estimate(root,
3912+
rel,
39083913
outer_path->parent,
39093914
inner_path->parent,
39103915
outer_path->rows,
@@ -3928,6 +3933,7 @@ get_parameterized_joinrel_size(PlannerInfo *root, RelOptInfo *rel,
39283933
*/
39293934
static double
39303935
calc_joinrel_size_estimate(PlannerInfo *root,
3936+
RelOptInfo *joinrel,
39313937
RelOptInfo *outer_rel,
39323938
RelOptInfo *inner_rel,
39333939
double outer_rows,
@@ -3981,7 +3987,7 @@ calc_joinrel_size_estimate(PlannerInfo *root,
39813987
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
39823988

39833989
Assert(IsA(rinfo, RestrictInfo));
3984-
if (rinfo->is_pushed_down)
3990+
if (RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
39853991
pushedquals = lappend(pushedquals, rinfo);
39863992
else
39873993
joinquals = lappend(joinquals, rinfo);

src/backend/optimizer/path/joinpath.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,7 +1308,7 @@ hash_inner_and_outer(PlannerInfo *root,
13081308
* If processing an outer join, only use its own join clauses for
13091309
* hashing. For inner joins we need not be so picky.
13101310
*/
1311-
if (isouterjoin && restrictinfo->is_pushed_down)
1311+
if (isouterjoin && RINFO_IS_PUSHED_DOWN(restrictinfo, joinrel->relids))
13121312
continue;
13131313

13141314
if (!restrictinfo->can_join ||
@@ -1547,7 +1547,7 @@ select_mergejoin_clauses(PlannerInfo *root,
15471547
* we don't set have_nonmergeable_joinclause here because pushed-down
15481548
* clauses will become otherquals not joinquals.)
15491549
*/
1550-
if (isouterjoin && restrictinfo->is_pushed_down)
1550+
if (isouterjoin && RINFO_IS_PUSHED_DOWN(restrictinfo, joinrel->relids))
15511551
continue;
15521552

15531553
/* Check that clause is a mergeable operator clause */

src/backend/optimizer/path/joinrels.c

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
3131
static bool is_dummy_rel(RelOptInfo *rel);
3232
static void mark_dummy_rel(RelOptInfo *rel);
3333
static bool restriction_is_constant_false(List *restrictlist,
34+
RelOptInfo *joinrel,
3435
bool only_pushed_down);
3536

3637

@@ -746,7 +747,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
746747
{
747748
case JOIN_INNER:
748749
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
749-
restriction_is_constant_false(restrictlist, false))
750+
restriction_is_constant_false(restrictlist, joinrel, false))
750751
{
751752
mark_dummy_rel(joinrel);
752753
break;
@@ -760,12 +761,12 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
760761
break;
761762
case JOIN_LEFT:
762763
if (is_dummy_rel(rel1) ||
763-
restriction_is_constant_false(restrictlist, true))
764+
restriction_is_constant_false(restrictlist, joinrel, true))
764765
{
765766
mark_dummy_rel(joinrel);
766767
break;
767768
}
768-
if (restriction_is_constant_false(restrictlist, false) &&
769+
if (restriction_is_constant_false(restrictlist, joinrel, false) &&
769770
bms_is_subset(rel2->relids, sjinfo->syn_righthand))
770771
mark_dummy_rel(rel2);
771772
add_paths_to_joinrel(root, joinrel, rel1, rel2,
@@ -777,7 +778,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
777778
break;
778779
case JOIN_FULL:
779780
if ((is_dummy_rel(rel1) && is_dummy_rel(rel2)) ||
780-
restriction_is_constant_false(restrictlist, true))
781+
restriction_is_constant_false(restrictlist, joinrel, true))
781782
{
782783
mark_dummy_rel(joinrel);
783784
break;
@@ -813,7 +814,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
813814
bms_is_subset(sjinfo->min_righthand, rel2->relids))
814815
{
815816
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
816-
restriction_is_constant_false(restrictlist, false))
817+
restriction_is_constant_false(restrictlist, joinrel, false))
817818
{
818819
mark_dummy_rel(joinrel);
819820
break;
@@ -836,7 +837,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
836837
sjinfo) != NULL)
837838
{
838839
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
839-
restriction_is_constant_false(restrictlist, false))
840+
restriction_is_constant_false(restrictlist, joinrel, false))
840841
{
841842
mark_dummy_rel(joinrel);
842843
break;
@@ -851,12 +852,12 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
851852
break;
852853
case JOIN_ANTI:
853854
if (is_dummy_rel(rel1) ||
854-
restriction_is_constant_false(restrictlist, true))
855+
restriction_is_constant_false(restrictlist, joinrel, true))
855856
{
856857
mark_dummy_rel(joinrel);
857858
break;
858859
}
859-
if (restriction_is_constant_false(restrictlist, false) &&
860+
if (restriction_is_constant_false(restrictlist, joinrel, false) &&
860861
bms_is_subset(rel2->relids, sjinfo->syn_righthand))
861862
mark_dummy_rel(rel2);
862863
add_paths_to_joinrel(root, joinrel, rel1, rel2,
@@ -1207,18 +1208,21 @@ mark_dummy_rel(RelOptInfo *rel)
12071208

12081209

12091210
/*
1210-
* restriction_is_constant_false --- is a restrictlist just FALSE?
1211+
* restriction_is_constant_false --- is a restrictlist just false?
12111212
*
1212-
* In cases where a qual is provably constant FALSE, eval_const_expressions
1213+
* In cases where a qual is provably constant false, eval_const_expressions
12131214
* will generally have thrown away anything that's ANDed with it. In outer
12141215
* join situations this will leave us computing cartesian products only to
12151216
* decide there's no match for an outer row, which is pretty stupid. So,
12161217
* we need to detect the case.
12171218
*
1218-
* If only_pushed_down is TRUE, then consider only pushed-down quals.
1219+
* If only_pushed_down is true, then consider only quals that are pushed-down
1220+
* from the point of view of the joinrel.
12191221
*/
12201222
static bool
1221-
restriction_is_constant_false(List *restrictlist, bool only_pushed_down)
1223+
restriction_is_constant_false(List *restrictlist,
1224+
RelOptInfo *joinrel,
1225+
bool only_pushed_down)
12221226
{
12231227
ListCell *lc;
12241228

@@ -1233,7 +1237,7 @@ restriction_is_constant_false(List *restrictlist, bool only_pushed_down)
12331237
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
12341238

12351239
Assert(IsA(rinfo, RestrictInfo));
1236-
if (only_pushed_down && !rinfo->is_pushed_down)
1240+
if (only_pushed_down && !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
12371241
continue;
12381242

12391243
if (rinfo->clause && IsA(rinfo->clause, Const))

src/backend/optimizer/plan/analyzejoins.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,7 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo)
248248
* above the outer join, even if it references no other rels (it might
249249
* be from WHERE, for example).
250250
*/
251-
if (restrictinfo->is_pushed_down ||
252-
!bms_equal(restrictinfo->required_relids, joinrelids))
251+
if (RINFO_IS_PUSHED_DOWN(restrictinfo, joinrelids))
253252
{
254253
/*
255254
* If such a clause actually references the inner rel then join
@@ -417,8 +416,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids)
417416

418417
remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
419418

420-
if (rinfo->is_pushed_down ||
421-
!bms_equal(rinfo->required_relids, joinrelids))
419+
if (RINFO_IS_PUSHED_DOWN(rinfo, joinrelids))
422420
{
423421
/* Recheck that qual doesn't actually reference the target rel */
424422
Assert(!bms_is_member(relid, rinfo->clause_relids));

src/backend/optimizer/plan/initsplan.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,6 +1676,11 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
16761676
* attach quals to the lowest level where they can be evaluated. But
16771677
* if we were ever to re-introduce a mechanism for delaying evaluation
16781678
* of "expensive" quals, this area would need work.
1679+
*
1680+
* Note: generally, use of is_pushed_down has to go through the macro
1681+
* RINFO_IS_PUSHED_DOWN, because that flag alone is not always sufficient
1682+
* to tell whether a clause must be treated as pushed-down in context.
1683+
* This seems like another reason why it should perhaps be rethought.
16791684
*----------
16801685
*/
16811686
if (is_deduced)

src/backend/optimizer/util/restrictinfo.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ extract_actual_clauses(List *restrictinfo_list,
410410
* extract_actual_join_clauses
411411
*
412412
* Extract bare clauses from 'restrictinfo_list', separating those that
413-
* syntactically match the join level from those that were pushed down.
413+
* semantically match the join level from those that were pushed down.
414414
* Pseudoconstant clauses are excluded from the results.
415415
*
416416
* This is only used at outer joins, since for plain joins we don't care
@@ -433,15 +433,7 @@ extract_actual_join_clauses(List *restrictinfo_list,
433433

434434
Assert(IsA(rinfo, RestrictInfo));
435435

436-
/*
437-
* We must check both is_pushed_down and required_relids, since an
438-
* outer-join clause that's been pushed down to some lower join level
439-
* via path parameterization will not be marked is_pushed_down;
440-
* nonetheless, it must be treated as a filter clause not a join
441-
* clause so far as the lower join level is concerned.
442-
*/
443-
if (rinfo->is_pushed_down ||
444-
!bms_is_subset(rinfo->required_relids, joinrelids))
436+
if (RINFO_IS_PUSHED_DOWN(rinfo, joinrelids))
445437
{
446438
if (!rinfo->pseudoconstant)
447439
*otherquals = lappend(*otherquals, rinfo->clause);

src/include/nodes/relation.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,8 @@ typedef struct LimitPath
15371537
* if we decide that it can be pushed down into the nullable side of the join.
15381538
* In that case it acts as a plain filter qual for wherever it gets evaluated.
15391539
* (In short, is_pushed_down is only false for non-degenerate outer join
1540-
* conditions. Possibly we should rename it to reflect that meaning?)
1540+
* conditions. Possibly we should rename it to reflect that meaning? But
1541+
* see also the comments for RINFO_IS_PUSHED_DOWN, below.)
15411542
*
15421543
* RestrictInfo nodes also contain an outerjoin_delayed flag, which is true
15431544
* if the clause's applicability must be delayed due to any outer joins
@@ -1664,6 +1665,20 @@ typedef struct RestrictInfo
16641665
Selectivity right_bucketsize; /* avg bucketsize of right side */
16651666
} RestrictInfo;
16661667

1668+
/*
1669+
* This macro embodies the correct way to test whether a RestrictInfo is
1670+
* "pushed down" to a given outer join, that is, should be treated as a filter
1671+
* clause rather than a join clause at that outer join. This is certainly so
1672+
* if is_pushed_down is true; but examining that is not sufficient anymore,
1673+
* because outer-join clauses will get pushed down to lower outer joins when
1674+
* we generate a path for the lower outer join that is parameterized by the
1675+
* LHS of the upper one. We can detect such a clause by noting that its
1676+
* required_relids exceed the scope of the join.
1677+
*/
1678+
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids) \
1679+
((rinfo)->is_pushed_down || \
1680+
!bms_is_subset((rinfo)->required_relids, joinrelids))
1681+
16671682
/*
16681683
* Since mergejoinscansel() is a relatively expensive function, and would
16691684
* otherwise be invoked many times while planning a large join tree,

0 commit comments

Comments
 (0)