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

Commit 8b6294c

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 68fab04 commit 8b6294c

File tree

8 files changed

+57
-34
lines changed

8 files changed

+57
-34
lines changed

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4127,7 +4127,8 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
41274127
bool is_remote_clause = is_foreign_expr(root, joinrel,
41284128
rinfo->clause);
41294129

4130-
if (IS_OUTER_JOIN(jointype) && !rinfo->is_pushed_down)
4130+
if (IS_OUTER_JOIN(jointype) &&
4131+
!RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
41314132
{
41324133
if (!is_remote_clause)
41334134
return false;

src/backend/optimizer/path/costsize.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ static bool has_indexed_join_quals(NestPath *joinpath);
148148
static double approx_tuple_count(PlannerInfo *root, JoinPath *path,
149149
List *quals);
150150
static double calc_joinrel_size_estimate(PlannerInfo *root,
151+
RelOptInfo *joinrel,
151152
RelOptInfo *outer_rel,
152153
RelOptInfo *inner_rel,
153154
double outer_rows,
@@ -3776,12 +3777,14 @@ compute_semi_anti_join_factors(PlannerInfo *root,
37763777
*/
37773778
if (IS_OUTER_JOIN(jointype))
37783779
{
3780+
Relids joinrelids = bms_union(outerrel->relids, innerrel->relids);
3781+
37793782
joinquals = NIL;
37803783
foreach(l, restrictlist)
37813784
{
37823785
RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
37833786

3784-
if (!rinfo->is_pushed_down)
3787+
if (!RINFO_IS_PUSHED_DOWN(rinfo, joinrelids))
37853788
joinquals = lappend(joinquals, rinfo);
37863789
}
37873790
}
@@ -4096,6 +4099,7 @@ set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
40964099
List *restrictlist)
40974100
{
40984101
rel->rows = calc_joinrel_size_estimate(root,
4102+
rel,
40994103
outer_rel,
41004104
inner_rel,
41014105
outer_rel->rows,
@@ -4138,6 +4142,7 @@ get_parameterized_joinrel_size(PlannerInfo *root, RelOptInfo *rel,
41384142
* estimate for any pair with the same parameterization.
41394143
*/
41404144
nrows = calc_joinrel_size_estimate(root,
4145+
rel,
41414146
outer_path->parent,
41424147
inner_path->parent,
41434148
outer_path->rows,
@@ -4161,6 +4166,7 @@ get_parameterized_joinrel_size(PlannerInfo *root, RelOptInfo *rel,
41614166
*/
41624167
static double
41634168
calc_joinrel_size_estimate(PlannerInfo *root,
4169+
RelOptInfo *joinrel,
41644170
RelOptInfo *outer_rel,
41654171
RelOptInfo *inner_rel,
41664172
double outer_rows,
@@ -4213,7 +4219,7 @@ calc_joinrel_size_estimate(PlannerInfo *root,
42134219
{
42144220
RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
42154221

4216-
if (rinfo->is_pushed_down)
4222+
if (RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
42174223
pushedquals = lappend(pushedquals, rinfo);
42184224
else
42194225
joinquals = lappend(joinquals, rinfo);

src/backend/optimizer/path/joinpath.c

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

16121612
if (!restrictinfo->can_join ||
@@ -1832,7 +1832,7 @@ select_mergejoin_clauses(PlannerInfo *root,
18321832
* we don't set have_nonmergeable_joinclause here because pushed-down
18331833
* clauses will become otherquals not joinquals.)
18341834
*/
1835-
if (isouterjoin && restrictinfo->is_pushed_down)
1835+
if (isouterjoin && RINFO_IS_PUSHED_DOWN(restrictinfo, joinrel->relids))
18361836
continue;
18371837

18381838
/* 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
static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
3637
RelOptInfo *rel2, RelOptInfo *joinrel,
@@ -770,7 +771,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
770771
{
771772
case JOIN_INNER:
772773
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
773-
restriction_is_constant_false(restrictlist, false))
774+
restriction_is_constant_false(restrictlist, joinrel, false))
774775
{
775776
mark_dummy_rel(joinrel);
776777
break;
@@ -784,12 +785,12 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
784785
break;
785786
case JOIN_LEFT:
786787
if (is_dummy_rel(rel1) ||
787-
restriction_is_constant_false(restrictlist, true))
788+
restriction_is_constant_false(restrictlist, joinrel, true))
788789
{
789790
mark_dummy_rel(joinrel);
790791
break;
791792
}
792-
if (restriction_is_constant_false(restrictlist, false) &&
793+
if (restriction_is_constant_false(restrictlist, joinrel, false) &&
793794
bms_is_subset(rel2->relids, sjinfo->syn_righthand))
794795
mark_dummy_rel(rel2);
795796
add_paths_to_joinrel(root, joinrel, rel1, rel2,
@@ -801,7 +802,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
801802
break;
802803
case JOIN_FULL:
803804
if ((is_dummy_rel(rel1) && is_dummy_rel(rel2)) ||
804-
restriction_is_constant_false(restrictlist, true))
805+
restriction_is_constant_false(restrictlist, joinrel, true))
805806
{
806807
mark_dummy_rel(joinrel);
807808
break;
@@ -837,7 +838,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
837838
bms_is_subset(sjinfo->min_righthand, rel2->relids))
838839
{
839840
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
840-
restriction_is_constant_false(restrictlist, false))
841+
restriction_is_constant_false(restrictlist, joinrel, false))
841842
{
842843
mark_dummy_rel(joinrel);
843844
break;
@@ -860,7 +861,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
860861
sjinfo) != NULL)
861862
{
862863
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
863-
restriction_is_constant_false(restrictlist, false))
864+
restriction_is_constant_false(restrictlist, joinrel, false))
864865
{
865866
mark_dummy_rel(joinrel);
866867
break;
@@ -875,12 +876,12 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
875876
break;
876877
case JOIN_ANTI:
877878
if (is_dummy_rel(rel1) ||
878-
restriction_is_constant_false(restrictlist, true))
879+
restriction_is_constant_false(restrictlist, joinrel, true))
879880
{
880881
mark_dummy_rel(joinrel);
881882
break;
882883
}
883-
if (restriction_is_constant_false(restrictlist, false) &&
884+
if (restriction_is_constant_false(restrictlist, joinrel, false) &&
884885
bms_is_subset(rel2->relids, sjinfo->syn_righthand))
885886
mark_dummy_rel(rel2);
886887
add_paths_to_joinrel(root, joinrel, rel1, rel2,
@@ -1227,18 +1228,21 @@ mark_dummy_rel(RelOptInfo *rel)
12271228

12281229

12291230
/*
1230-
* restriction_is_constant_false --- is a restrictlist just FALSE?
1231+
* restriction_is_constant_false --- is a restrictlist just false?
12311232
*
1232-
* In cases where a qual is provably constant FALSE, eval_const_expressions
1233+
* In cases where a qual is provably constant false, eval_const_expressions
12331234
* will generally have thrown away anything that's ANDed with it. In outer
12341235
* join situations this will leave us computing cartesian products only to
12351236
* decide there's no match for an outer row, which is pretty stupid. So,
12361237
* we need to detect the case.
12371238
*
1238-
* If only_pushed_down is TRUE, then consider only pushed-down quals.
1239+
* If only_pushed_down is true, then consider only quals that are pushed-down
1240+
* from the point of view of the joinrel.
12391241
*/
12401242
static bool
1241-
restriction_is_constant_false(List *restrictlist, bool only_pushed_down)
1243+
restriction_is_constant_false(List *restrictlist,
1244+
RelOptInfo *joinrel,
1245+
bool only_pushed_down)
12421246
{
12431247
ListCell *lc;
12441248

@@ -1252,7 +1256,7 @@ restriction_is_constant_false(List *restrictlist, bool only_pushed_down)
12521256
{
12531257
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
12541258

1255-
if (only_pushed_down && !rinfo->is_pushed_down)
1259+
if (only_pushed_down && !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
12561260
continue;
12571261

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

src/backend/optimizer/plan/analyzejoins.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,7 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo)
253253
* above the outer join, even if it references no other rels (it might
254254
* be from WHERE, for example).
255255
*/
256-
if (restrictinfo->is_pushed_down ||
257-
!bms_equal(restrictinfo->required_relids, joinrelids))
256+
if (RINFO_IS_PUSHED_DOWN(restrictinfo, joinrelids))
258257
{
259258
/*
260259
* If such a clause actually references the inner rel then join
@@ -422,8 +421,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids)
422421

423422
remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
424423

425-
if (rinfo->is_pushed_down ||
426-
!bms_equal(rinfo->required_relids, joinrelids))
424+
if (RINFO_IS_PUSHED_DOWN(rinfo, joinrelids))
427425
{
428426
/* Recheck that qual doesn't actually reference the target rel */
429427
Assert(!bms_is_member(relid, rinfo->clause_relids));
@@ -1080,6 +1078,7 @@ is_innerrel_unique_for(PlannerInfo *root,
10801078
JoinType jointype,
10811079
List *restrictlist)
10821080
{
1081+
Relids joinrelids = bms_union(outerrelids, innerrel->relids);
10831082
List *clause_list = NIL;
10841083
ListCell *lc;
10851084

@@ -1098,7 +1097,8 @@ is_innerrel_unique_for(PlannerInfo *root,
10981097
* As noted above, if it's a pushed-down clause and we're at an outer
10991098
* join, we can't use it.
11001099
*/
1101-
if (restrictinfo->is_pushed_down && IS_OUTER_JOIN(jointype))
1100+
if (IS_OUTER_JOIN(jointype) &&
1101+
RINFO_IS_PUSHED_DOWN(restrictinfo, joinrelids))
11021102
continue;
11031103

11041104
/* Ignore if it's not a mergejoinable clause */

src/backend/optimizer/plan/initsplan.c

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

src/backend/optimizer/util/restrictinfo.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ extract_actual_clauses(List *restrictinfo_list,
371371
* extract_actual_join_clauses
372372
*
373373
* Extract bare clauses from 'restrictinfo_list', separating those that
374-
* syntactically match the join level from those that were pushed down.
374+
* semantically match the join level from those that were pushed down.
375375
* Pseudoconstant clauses are excluded from the results.
376376
*
377377
* This is only used at outer joins, since for plain joins we don't care
@@ -392,15 +392,7 @@ extract_actual_join_clauses(List *restrictinfo_list,
392392
{
393393
RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
394394

395-
/*
396-
* We must check both is_pushed_down and required_relids, since an
397-
* outer-join clause that's been pushed down to some lower join level
398-
* via path parameterization will not be marked is_pushed_down;
399-
* nonetheless, it must be treated as a filter clause not a join
400-
* clause so far as the lower join level is concerned.
401-
*/
402-
if (rinfo->is_pushed_down ||
403-
!bms_is_subset(rinfo->required_relids, joinrelids))
395+
if (RINFO_IS_PUSHED_DOWN(rinfo, joinrelids))
404396
{
405397
if (!rinfo->pseudoconstant)
406398
*otherquals = lappend(*otherquals, rinfo->clause);

src/include/nodes/relation.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1669,7 +1669,8 @@ typedef struct LimitPath
16691669
* if we decide that it can be pushed down into the nullable side of the join.
16701670
* In that case it acts as a plain filter qual for wherever it gets evaluated.
16711671
* (In short, is_pushed_down is only false for non-degenerate outer join
1672-
* conditions. Possibly we should rename it to reflect that meaning?)
1672+
* conditions. Possibly we should rename it to reflect that meaning? But
1673+
* see also the comments for RINFO_IS_PUSHED_DOWN, below.)
16731674
*
16741675
* RestrictInfo nodes also contain an outerjoin_delayed flag, which is true
16751676
* if the clause's applicability must be delayed due to any outer joins
@@ -1809,6 +1810,20 @@ typedef struct RestrictInfo
18091810
Selectivity right_bucketsize; /* avg bucketsize of right side */
18101811
} RestrictInfo;
18111812

1813+
/*
1814+
* This macro embodies the correct way to test whether a RestrictInfo is
1815+
* "pushed down" to a given outer join, that is, should be treated as a filter
1816+
* clause rather than a join clause at that outer join. This is certainly so
1817+
* if is_pushed_down is true; but examining that is not sufficient anymore,
1818+
* because outer-join clauses will get pushed down to lower outer joins when
1819+
* we generate a path for the lower outer join that is parameterized by the
1820+
* LHS of the upper one. We can detect such a clause by noting that its
1821+
* required_relids exceed the scope of the join.
1822+
*/
1823+
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids) \
1824+
((rinfo)->is_pushed_down || \
1825+
!bms_is_subset((rinfo)->required_relids, joinrelids))
1826+
18121827
/*
18131828
* Since mergejoinscansel() is a relatively expensive function, and would
18141829
* otherwise be invoked many times while planning a large join tree,

0 commit comments

Comments
 (0)