|
16 | 16 | *
|
17 | 17 | *
|
18 | 18 | * IDENTIFICATION
|
19 |
| - * $PostgreSQL: pgsql/src/backend/optimizer/plan/analyzejoins.c,v 1.3 2010/07/06 19:18:56 momjian Exp $ |
| 19 | + * $PostgreSQL: pgsql/src/backend/optimizer/plan/analyzejoins.c,v 1.4 2010/09/14 23:15:29 tgl Exp $ |
20 | 20 | *
|
21 | 21 | *-------------------------------------------------------------------------
|
22 | 22 | */
|
23 | 23 | #include "postgres.h"
|
24 | 24 |
|
| 25 | +#include "optimizer/joininfo.h" |
25 | 26 | #include "optimizer/pathnode.h"
|
26 | 27 | #include "optimizer/paths.h"
|
27 | 28 | #include "optimizer/planmain.h"
|
28 | 29 |
|
29 | 30 | /* local functions */
|
30 | 31 | static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
|
31 |
| -static void remove_rel_from_query(PlannerInfo *root, int relid); |
| 32 | +static void remove_rel_from_query(PlannerInfo *root, int relid, |
| 33 | + Relids joinrelids); |
32 | 34 | static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
|
33 | 35 |
|
34 | 36 |
|
@@ -67,7 +69,9 @@ remove_useless_joins(PlannerInfo *root, List *joinlist)
|
67 | 69 | */
|
68 | 70 | innerrelid = bms_singleton_member(sjinfo->min_righthand);
|
69 | 71 |
|
70 |
| - remove_rel_from_query(root, innerrelid); |
| 72 | + remove_rel_from_query(root, innerrelid, |
| 73 | + bms_union(sjinfo->min_lefthand, |
| 74 | + sjinfo->min_righthand)); |
71 | 75 |
|
72 | 76 | /* We verify that exactly one reference gets removed from joinlist */
|
73 | 77 | nremoved = 0;
|
@@ -216,19 +220,25 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo)
|
216 | 220 | {
|
217 | 221 | RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
|
218 | 222 |
|
219 |
| - /* Ignore clauses not pertinent to this join */ |
220 |
| - if (!bms_is_subset(restrictinfo->required_relids, joinrelids)) |
221 |
| - continue; |
222 |
| - |
223 | 223 | /*
|
224 |
| - * If we find a pushed-down clause, it must have come from above the |
225 |
| - * outer join and it must contain references to the inner rel. (If it |
226 |
| - * had only outer-rel variables, it'd have been pushed down into the |
227 |
| - * outer rel.) Therefore, we can conclude that join removal is unsafe |
228 |
| - * without any examination of the clause contents. |
| 224 | + * If it's not a join clause for this outer join, we can't use it. |
| 225 | + * Note that if the clause is pushed-down, then it is logically from |
| 226 | + * above the outer join, even if it references no other rels (it might |
| 227 | + * be from WHERE, for example). |
229 | 228 | */
|
230 |
| - if (restrictinfo->is_pushed_down) |
231 |
| - return false; |
| 229 | + if (restrictinfo->is_pushed_down || |
| 230 | + !bms_equal(restrictinfo->required_relids, joinrelids)) |
| 231 | + { |
| 232 | + /* |
| 233 | + * If such a clause actually references the inner rel then |
| 234 | + * join removal has to be disallowed. We have to check this |
| 235 | + * despite the previous attr_needed checks because of the |
| 236 | + * possibility of pushed-down clauses referencing the rel. |
| 237 | + */ |
| 238 | + if (bms_is_member(innerrelid, restrictinfo->clause_relids)) |
| 239 | + return false; |
| 240 | + continue; /* else, ignore; not useful here */ |
| 241 | + } |
232 | 242 |
|
233 | 243 | /* Ignore if it's not a mergejoinable clause */
|
234 | 244 | if (!restrictinfo->can_join ||
|
@@ -299,14 +309,14 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo)
|
299 | 309 | * We are not terribly thorough here. We must make sure that the rel is
|
300 | 310 | * no longer treated as a baserel, and that attributes of other baserels
|
301 | 311 | * are no longer marked as being needed at joins involving this rel.
|
302 |
| - * In particular, we don't bother removing join quals involving the rel from |
303 |
| - * the joininfo lists; they'll just get ignored, since we will never form a |
304 |
| - * join relation at which they could be evaluated. |
| 312 | + * Also, join quals involving the rel have to be removed from the joininfo |
| 313 | + * lists, but only if they belong to the outer join identified by joinrelids. |
305 | 314 | */
|
306 | 315 | static void
|
307 |
| -remove_rel_from_query(PlannerInfo *root, int relid) |
| 316 | +remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids) |
308 | 317 | {
|
309 | 318 | RelOptInfo *rel = find_base_rel(root, relid);
|
| 319 | + List *joininfos; |
310 | 320 | Index rti;
|
311 | 321 | ListCell *l;
|
312 | 322 |
|
@@ -379,6 +389,43 @@ remove_rel_from_query(PlannerInfo *root, int relid)
|
379 | 389 |
|
380 | 390 | phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid);
|
381 | 391 | }
|
| 392 | + |
| 393 | + /* |
| 394 | + * Remove any joinquals referencing the rel from the joininfo lists. |
| 395 | + * |
| 396 | + * In some cases, a joinqual has to be put back after deleting its |
| 397 | + * reference to the target rel. This can occur for pseudoconstant and |
| 398 | + * outerjoin-delayed quals, which can get marked as requiring the rel in |
| 399 | + * order to force them to be evaluated at or above the join. We can't |
| 400 | + * just discard them, though. Only quals that logically belonged to the |
| 401 | + * outer join being discarded should be removed from the query. |
| 402 | + * |
| 403 | + * We must make a copy of the rel's old joininfo list before starting the |
| 404 | + * loop, because otherwise remove_join_clause_from_rels would destroy the |
| 405 | + * list while we're scanning it. |
| 406 | + */ |
| 407 | + joininfos = list_copy(rel->joininfo); |
| 408 | + foreach(l, joininfos) |
| 409 | + { |
| 410 | + RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); |
| 411 | + |
| 412 | + remove_join_clause_from_rels(root, rinfo, rinfo->required_relids); |
| 413 | + |
| 414 | + if (rinfo->is_pushed_down || |
| 415 | + !bms_equal(rinfo->required_relids, joinrelids)) |
| 416 | + { |
| 417 | + /* Recheck that qual doesn't actually reference the target rel */ |
| 418 | + Assert(!bms_is_member(relid, rinfo->clause_relids)); |
| 419 | + /* |
| 420 | + * The required_relids probably aren't shared with anything else, |
| 421 | + * but let's copy them just to be sure. |
| 422 | + */ |
| 423 | + rinfo->required_relids = bms_copy(rinfo->required_relids); |
| 424 | + rinfo->required_relids = bms_del_member(rinfo->required_relids, |
| 425 | + relid); |
| 426 | + distribute_restrictinfo_to_rels(root, rinfo); |
| 427 | + } |
| 428 | + } |
382 | 429 | }
|
383 | 430 |
|
384 | 431 | /*
|
|
0 commit comments