8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.84 2003/02/08 20:20:54 tgl Exp $
11
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.85 2003/03/02 23:46:34 tgl Exp $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
36
36
static void mark_baserels_for_outer_join (Query * root , Relids rels ,
37
37
Relids outerrels );
38
38
static void distribute_qual_to_rels (Query * root , Node * clause ,
39
- bool ispusheddown ,
40
- bool isouterjoin ,
41
- bool isdeduced ,
42
- Relids qualscope );
39
+ bool ispusheddown ,
40
+ bool isdeduced ,
41
+ Relids outerjoin_nonnullable ,
42
+ Relids qualscope );
43
43
static void add_vars_to_targetlist (Query * root , List * vars );
44
44
static bool qual_is_redundant (Query * root , RestrictInfo * restrictinfo ,
45
45
List * restrictlist );
@@ -209,48 +209,53 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
209
209
*/
210
210
foreach (qual , (List * ) f -> quals )
211
211
distribute_qual_to_rels (root , (Node * ) lfirst (qual ),
212
- true, false, false , result );
212
+ true, false, NULL , result );
213
213
}
214
214
else if (IsA (jtnode , JoinExpr ))
215
215
{
216
216
JoinExpr * j = (JoinExpr * ) jtnode ;
217
217
Relids leftids ,
218
- rightids ;
219
- bool isouterjoin ;
218
+ rightids ,
219
+ nonnullable_rels ,
220
+ nullable_rels ;
220
221
List * qual ;
221
222
222
223
/*
223
224
* Order of operations here is subtle and critical. First we
224
225
* recurse to handle sub-JOINs. Their join quals will be placed
225
226
* without regard for whether this level is an outer join, which
226
- * is correct. Then, if we are an outer join, we mark baserels
227
- * contained within the nullable side(s) with our own rel set;
228
- * this will restrict placement of subsequent quals using those
229
- * rels, including our own quals and quals above us in the join
230
- * tree. Finally we place our own join quals.
227
+ * is correct. Then we place our own join quals, which are restricted
228
+ * by lower outer joins in any case, and are forced to this level if
229
+ * this is an outer join and they mention the outer side. Finally, if
230
+ * this is an outer join, we mark baserels contained within the inner
231
+ * side(s) with our own rel set; this will prevent quals above us in
232
+ * the join tree that use those rels from being pushed down below this
233
+ * level. (It's okay for upper quals to be pushed down to the outer
234
+ * side, however.)
231
235
*/
232
236
leftids = distribute_quals_to_rels (root , j -> larg );
233
237
rightids = distribute_quals_to_rels (root , j -> rarg );
234
238
235
239
result = bms_union (leftids , rightids );
236
240
237
- isouterjoin = false ;
241
+ nonnullable_rels = nullable_rels = NULL ;
238
242
switch (j -> jointype )
239
243
{
240
244
case JOIN_INNER :
241
245
/* Inner join adds no restrictions for quals */
242
246
break ;
243
247
case JOIN_LEFT :
244
- mark_baserels_for_outer_join ( root , rightids , result ) ;
245
- isouterjoin = true ;
248
+ nonnullable_rels = leftids ;
249
+ nullable_rels = rightids ;
246
250
break ;
247
251
case JOIN_FULL :
248
- mark_baserels_for_outer_join (root , result , result );
249
- isouterjoin = true;
252
+ /* each side is both outer and inner */
253
+ nonnullable_rels = result ;
254
+ nullable_rels = result ;
250
255
break ;
251
256
case JOIN_RIGHT :
252
- mark_baserels_for_outer_join ( root , leftids , result ) ;
253
- isouterjoin = true ;
257
+ nonnullable_rels = rightids ;
258
+ nullable_rels = leftids ;
254
259
break ;
255
260
case JOIN_UNION :
256
261
@@ -269,7 +274,11 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
269
274
270
275
foreach (qual , (List * ) j -> quals )
271
276
distribute_qual_to_rels (root , (Node * ) lfirst (qual ),
272
- false, isouterjoin , false, result );
277
+ false, false,
278
+ nonnullable_rels , result );
279
+
280
+ if (nullable_rels != NULL )
281
+ mark_baserels_for_outer_join (root , nullable_rels , result );
273
282
}
274
283
else
275
284
elog (ERROR , "distribute_quals_to_rels: unexpected node type %d" ,
@@ -324,14 +333,16 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
324
333
* (depending on whether the clause is a join) of each base relation
325
334
* mentioned in the clause. A RestrictInfo node is created and added to
326
335
* the appropriate list for each rel. Also, if the clause uses a
327
- * mergejoinable operator and is not an outer-join qual, enter the left-
328
- * and right-side expressions into the query's lists of equijoined vars.
336
+ * mergejoinable operator and is not delayed by outer-join rules, enter
337
+ * the left- and right-side expressions into the query's lists of
338
+ * equijoined vars.
329
339
*
330
340
* 'clause': the qual clause to be distributed
331
341
* 'ispusheddown': if TRUE, force the clause to be marked 'ispusheddown'
332
342
* (this indicates the clause came from a FromExpr, not a JoinExpr)
333
- * 'isouterjoin': TRUE if the qual came from an OUTER JOIN's ON-clause
334
343
* 'isdeduced': TRUE if the qual came from implied-equality deduction
344
+ * 'outerjoin_nonnullable': NULL if not an outer-join qual, else the set of
345
+ * baserels appearing on the outer (nonnullable) side of the join
335
346
* 'qualscope': set of baserels the qual's syntactic scope covers
336
347
*
337
348
* 'qualscope' identifies what level of JOIN the qual came from. For a top
@@ -341,8 +352,8 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
341
352
static void
342
353
distribute_qual_to_rels (Query * root , Node * clause ,
343
354
bool ispusheddown ,
344
- bool isouterjoin ,
345
355
bool isdeduced ,
356
+ Relids outerjoin_nonnullable ,
346
357
Relids qualscope )
347
358
{
348
359
RestrictInfo * restrictinfo = makeNode (RestrictInfo );
@@ -392,63 +403,80 @@ distribute_qual_to_rels(Query *root, Node *clause,
392
403
relids = qualscope ;
393
404
394
405
/*
395
- * For an outer-join qual, pretend that the clause references all rels
396
- * appearing within its syntactic scope, even if it really doesn't.
397
- * This ensures that the clause will be evaluated exactly at the level
398
- * of joining corresponding to the outer join.
399
- *
400
- * For a non-outer-join qual, we can evaluate the qual as soon as (1) we
401
- * have all the rels it mentions, and (2) we are at or above any outer
402
- * joins that can null any of these rels and are below the syntactic
403
- * location of the given qual. To enforce the latter, scan the base
404
- * rels listed in relids, and merge their outer-join sets into the
405
- * clause's own reference list. At the time we are called, the
406
- * outerjoinset of each baserel will show exactly those outer
407
- * joins that are below the qual in the join tree.
408
- *
409
- * If the qual came from implied-equality deduction, we can evaluate the
410
- * qual at its natural semantic level.
411
- *
406
+ * Check to see if clause application must be delayed by outer-join
407
+ * considerations.
412
408
*/
413
409
if (isdeduced )
414
410
{
411
+ /*
412
+ * If the qual came from implied-equality deduction, we can evaluate
413
+ * the qual at its natural semantic level. It is not affected by
414
+ * any outer-join rules (else we'd not have decided the vars were
415
+ * equal).
416
+ */
415
417
Assert (bms_equal (relids , qualscope ));
416
418
can_be_equijoin = true;
417
419
}
418
- else if (isouterjoin )
420
+ else if (bms_overlap ( relids , outerjoin_nonnullable ) )
419
421
{
422
+ /*
423
+ * The qual is attached to an outer join and mentions (some of the)
424
+ * rels on the nonnullable side. Force the qual to be evaluated
425
+ * exactly at the level of joining corresponding to the outer join.
426
+ * We cannot let it get pushed down into the nonnullable side, since
427
+ * then we'd produce no output rows, rather than the intended single
428
+ * null-extended row, for any nonnullable-side rows failing the qual.
429
+ *
430
+ * Note: an outer-join qual that mentions only nullable-side rels can
431
+ * be pushed down into the nullable side without changing the join
432
+ * result, so we treat it the same as an ordinary inner-join qual.
433
+ */
420
434
relids = qualscope ;
421
435
can_be_equijoin = false;
422
436
}
423
437
else
424
438
{
425
- /* copy to ensure we don't change caller's qualscope set */
426
- Relids newrelids = bms_copy (relids );
439
+ /*
440
+ * For a non-outer-join qual, we can evaluate the qual as soon as
441
+ * (1) we have all the rels it mentions, and (2) we are at or above
442
+ * any outer joins that can null any of these rels and are below the
443
+ * syntactic location of the given qual. To enforce the latter, scan
444
+ * the base rels listed in relids, and merge their outer-join sets
445
+ * into the clause's own reference list. At the time we are called,
446
+ * the outerjoinset of each baserel will show exactly those outer
447
+ * joins that are below the qual in the join tree.
448
+ */
449
+ Relids addrelids = NULL ;
427
450
Relids tmprelids ;
428
451
int relno ;
429
452
430
- can_be_equijoin = true;
431
453
tmprelids = bms_copy (relids );
432
454
while ((relno = bms_first_member (tmprelids )) >= 0 )
433
455
{
434
456
RelOptInfo * rel = find_base_rel (root , relno );
435
457
436
- if (!bms_is_subset (rel -> outerjoinset , relids ))
437
- {
438
- newrelids = bms_add_members (newrelids , rel -> outerjoinset );
439
-
440
- /*
441
- * Because application of the qual will be delayed by
442
- * outer join, we mustn't assume its vars are equal
443
- * everywhere.
444
- */
445
- can_be_equijoin = false;
446
- }
458
+ if (rel -> outerjoinset != NULL )
459
+ addrelids = bms_add_members (addrelids , rel -> outerjoinset );
447
460
}
448
461
bms_free (tmprelids );
449
- relids = newrelids ;
450
- /* Should still be a subset of current scope ... */
451
- Assert (bms_is_subset (relids , qualscope ));
462
+
463
+ if (bms_is_subset (addrelids , relids ))
464
+ {
465
+ /* Qual is not affected by any outer-join restriction */
466
+ can_be_equijoin = true;
467
+ }
468
+ else
469
+ {
470
+ relids = bms_union (relids , addrelids );
471
+ /* Should still be a subset of current scope ... */
472
+ Assert (bms_is_subset (relids , qualscope ));
473
+ /*
474
+ * Because application of the qual will be delayed by outer join,
475
+ * we mustn't assume its vars are equal everywhere.
476
+ */
477
+ can_be_equijoin = false;
478
+ }
479
+ bms_free (addrelids );
452
480
}
453
481
454
482
/*
@@ -725,8 +753,7 @@ process_implied_equality(Query *root,
725
753
* taken for an original JOIN/ON clause.
726
754
*/
727
755
distribute_qual_to_rels (root , (Node * ) clause ,
728
- true, false, true,
729
- relids );
756
+ true, true, NULL , relids );
730
757
}
731
758
732
759
/*
0 commit comments