~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/optimizer/prep/prepjointree.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * prepjointree.c
 
4
 *        Planner preprocessing for subqueries and join tree manipulation.
 
5
 *
 
6
 * NOTE: the intended sequence for invoking these operations is
 
7
 *              pull_up_IN_clauses
 
8
 *              pull_up_subqueries
 
9
 *              do expression preprocessing (including flattening JOIN alias vars)
 
10
 *              reduce_outer_joins
 
11
 *              simplify_jointree
 
12
 *
 
13
 *
 
14
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 
15
 * Portions Copyright (c) 1994, Regents of the University of California
 
16
 *
 
17
 *
 
18
 * IDENTIFICATION
 
19
 *        $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.25 2004-12-31 22:00:20 pgsql Exp $
 
20
 *
 
21
 *-------------------------------------------------------------------------
 
22
 */
 
23
#include "postgres.h"
 
24
 
 
25
#include "optimizer/clauses.h"
 
26
#include "optimizer/prep.h"
 
27
#include "optimizer/subselect.h"
 
28
#include "optimizer/var.h"
 
29
#include "parser/parsetree.h"
 
30
#include "rewrite/rewriteManip.h"
 
31
#include "utils/lsyscache.h"
 
32
 
 
33
 
 
34
/* These parameters are set by GUC */
 
35
int                     from_collapse_limit;
 
36
int                     join_collapse_limit;
 
37
 
 
38
 
 
39
typedef struct reduce_outer_joins_state
 
40
{
 
41
        Relids          relids;                 /* base relids within this subtree */
 
42
        bool            contains_outer; /* does subtree contain outer join(s)? */
 
43
        List       *sub_states;         /* List of states for subtree components */
 
44
} reduce_outer_joins_state;
 
45
 
 
46
static bool is_simple_subquery(Query *subquery);
 
47
static bool has_nullable_targetlist(Query *subquery);
 
48
static void resolvenew_in_jointree(Node *jtnode, int varno,
 
49
                                           List *rtable, List *subtlist);
 
50
static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
 
51
static void reduce_outer_joins_pass2(Node *jtnode,
 
52
                                                 reduce_outer_joins_state *state,
 
53
                                                 Query *parse,
 
54
                                                 Relids nonnullable_rels);
 
55
static Relids find_nonnullable_rels(Node *node, bool top_level);
 
56
static void fix_in_clause_relids(List *in_info_list, int varno,
 
57
                                         Relids subrelids);
 
58
static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
 
59
 
 
60
 
 
61
/*
 
62
 * pull_up_IN_clauses
 
63
 *              Attempt to pull up top-level IN clauses to be treated like joins.
 
64
 *
 
65
 * A clause "foo IN (sub-SELECT)" appearing at the top level of WHERE can
 
66
 * be processed by pulling the sub-SELECT up to become a rangetable entry
 
67
 * and handling the implied equality comparisons as join operators (with
 
68
 * special join rules).
 
69
 * This optimization *only* works at the top level of WHERE, because
 
70
 * it cannot distinguish whether the IN ought to return FALSE or NULL in
 
71
 * cases involving NULL inputs.  This routine searches for such clauses
 
72
 * and does the necessary parsetree transformations if any are found.
 
73
 *
 
74
 * This routine has to run before preprocess_expression(), so the WHERE
 
75
 * clause is not yet reduced to implicit-AND format.  That means we need
 
76
 * to recursively search through explicit AND clauses, which are
 
77
 * probably only binary ANDs.  We stop as soon as we hit a non-AND item.
 
78
 *
 
79
 * Returns the possibly-modified version of the given qual-tree node.
 
80
 */
 
81
Node *
 
82
pull_up_IN_clauses(Query *parse, Node *node)
 
83
{
 
84
        if (node == NULL)
 
85
                return NULL;
 
86
        if (IsA(node, SubLink))
 
87
        {
 
88
                SubLink    *sublink = (SubLink *) node;
 
89
                Node       *subst;
 
90
 
 
91
                /* Is it a convertible IN clause?  If not, return it as-is */
 
92
                subst = convert_IN_to_join(parse, sublink);
 
93
                if (subst == NULL)
 
94
                        return node;
 
95
                return subst;
 
96
        }
 
97
        if (and_clause(node))
 
98
        {
 
99
                List       *newclauses = NIL;
 
100
                ListCell   *l;
 
101
 
 
102
                foreach(l, ((BoolExpr *) node)->args)
 
103
                {
 
104
                        Node       *oldclause = (Node *) lfirst(l);
 
105
 
 
106
                        newclauses = lappend(newclauses,
 
107
                                                                 pull_up_IN_clauses(parse,
 
108
                                                                                                        oldclause));
 
109
                }
 
110
                return (Node *) make_andclause(newclauses);
 
111
        }
 
112
        /* Stop if not an AND */
 
113
        return node;
 
114
}
 
115
 
 
116
/*
 
117
 * pull_up_subqueries
 
118
 *              Look for subqueries in the rangetable that can be pulled up into
 
119
 *              the parent query.  If the subquery has no special features like
 
120
 *              grouping/aggregation then we can merge it into the parent's jointree.
 
121
 *
 
122
 * below_outer_join is true if this jointree node is within the nullable
 
123
 * side of an outer join.  This restricts what we can do.
 
124
 *
 
125
 * A tricky aspect of this code is that if we pull up a subquery we have
 
126
 * to replace Vars that reference the subquery's outputs throughout the
 
127
 * parent query, including quals attached to jointree nodes above the one
 
128
 * we are currently processing!  We handle this by being careful not to
 
129
 * change the jointree structure while recursing: no nodes other than
 
130
 * subquery RangeTblRef entries will be replaced.  Also, we can't turn
 
131
 * ResolveNew loose on the whole jointree, because it'll return a mutated
 
132
 * copy of the tree; we have to invoke it just on the quals, instead.
 
133
 */
 
134
Node *
 
135
pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
 
136
{
 
137
        if (jtnode == NULL)
 
138
                return NULL;
 
139
        if (IsA(jtnode, RangeTblRef))
 
140
        {
 
141
                int                     varno = ((RangeTblRef *) jtnode)->rtindex;
 
142
                RangeTblEntry *rte = rt_fetch(varno, parse->rtable);
 
143
                Query      *subquery = rte->subquery;
 
144
 
 
145
                /*
 
146
                 * Is this a subquery RTE, and if so, is the subquery simple
 
147
                 * enough to pull up?  (If not, do nothing at this node.)
 
148
                 *
 
149
                 * If we are inside an outer join, only pull up subqueries whose
 
150
                 * targetlists are nullable --- otherwise substituting their tlist
 
151
                 * entries for upper Var references would do the wrong thing (the
 
152
                 * results wouldn't become NULL when they're supposed to).
 
153
                 *
 
154
                 * XXX This could be improved by generating pseudo-variables for such
 
155
                 * expressions; we'd have to figure out how to get the pseudo-
 
156
                 * variables evaluated at the right place in the modified plan
 
157
                 * tree. Fix it someday.
 
158
                 */
 
159
                if (rte->rtekind == RTE_SUBQUERY &&
 
160
                        is_simple_subquery(subquery) &&
 
161
                        (!below_outer_join || has_nullable_targetlist(subquery)))
 
162
                {
 
163
                        int                     rtoffset;
 
164
                        List       *subtlist;
 
165
                        ListCell   *rt;
 
166
 
 
167
                        /*
 
168
                         * Need a modifiable copy of the subquery to hack on.  Even if
 
169
                         * we didn't sometimes choose not to pull up below, we must do
 
170
                         * this to avoid problems if the same subquery is referenced
 
171
                         * from multiple jointree items (which can't happen normally,
 
172
                         * but might after rule rewriting).
 
173
                         */
 
174
                        subquery = copyObject(subquery);
 
175
 
 
176
                        /*
 
177
                         * Pull up any IN clauses within the subquery's WHERE, so that
 
178
                         * we don't leave unoptimized INs behind.
 
179
                         */
 
180
                        if (subquery->hasSubLinks)
 
181
                                subquery->jointree->quals = pull_up_IN_clauses(subquery,
 
182
                                                                                          subquery->jointree->quals);
 
183
 
 
184
                        /*
 
185
                         * Recursively pull up the subquery's subqueries, so that this
 
186
                         * routine's processing is complete for its jointree and
 
187
                         * rangetable.
 
188
                         *
 
189
                         * Note: 'false' is correct here even if we are within an outer
 
190
                         * join in the upper query; the lower query starts with a
 
191
                         * clean slate for outer-join semantics.
 
192
                         */
 
193
                        subquery->jointree = (FromExpr *)
 
194
                                pull_up_subqueries(subquery, (Node *) subquery->jointree,
 
195
                                                                   false);
 
196
 
 
197
                        /*
 
198
                         * Now we must recheck whether the subquery is still simple
 
199
                         * enough to pull up.  If not, abandon processing it.
 
200
                         *
 
201
                         * We don't really need to recheck all the conditions involved,
 
202
                         * but it's easier just to keep this "if" looking the same as
 
203
                         * the one above.
 
204
                         */
 
205
                        if (is_simple_subquery(subquery) &&
 
206
                                (!below_outer_join || has_nullable_targetlist(subquery)))
 
207
                        {
 
208
                                /* good to go */
 
209
                        }
 
210
                        else
 
211
                        {
 
212
                                /*
 
213
                                 * Give up, return unmodified RangeTblRef.
 
214
                                 *
 
215
                                 * Note: The work we just did will be redone when the
 
216
                                 * subquery gets planned on its own.  Perhaps we could
 
217
                                 * avoid that by storing the modified subquery back into
 
218
                                 * the rangetable, but I'm not gonna risk it now.
 
219
                                 */
 
220
                                return jtnode;
 
221
                        }
 
222
 
 
223
                        /*
 
224
                         * Adjust level-0 varnos in subquery so that we can append its
 
225
                         * rangetable to upper query's.
 
226
                         */
 
227
                        rtoffset = list_length(parse->rtable);
 
228
                        OffsetVarNodes((Node *) subquery, rtoffset, 0);
 
229
 
 
230
                        /*
 
231
                         * Upper-level vars in subquery are now one level closer to
 
232
                         * their parent than before.
 
233
                         */
 
234
                        IncrementVarSublevelsUp((Node *) subquery, -1, 1);
 
235
 
 
236
                        /*
 
237
                         * Replace all of the top query's references to the subquery's
 
238
                         * outputs with copies of the adjusted subtlist items, being
 
239
                         * careful not to replace any of the jointree structure.
 
240
                         * (This'd be a lot cleaner if we could use
 
241
                         * query_tree_mutator.)
 
242
                         */
 
243
                        subtlist = subquery->targetList;
 
244
                        parse->targetList = (List *)
 
245
                                ResolveNew((Node *) parse->targetList,
 
246
                                                   varno, 0, parse->rtable,
 
247
                                                   subtlist, CMD_SELECT, 0);
 
248
                        resolvenew_in_jointree((Node *) parse->jointree, varno,
 
249
                                                                   parse->rtable, subtlist);
 
250
                        Assert(parse->setOperations == NULL);
 
251
                        parse->havingQual =
 
252
                                ResolveNew(parse->havingQual,
 
253
                                                   varno, 0, parse->rtable,
 
254
                                                   subtlist, CMD_SELECT, 0);
 
255
                        parse->in_info_list = (List *)
 
256
                                ResolveNew((Node *) parse->in_info_list,
 
257
                                                   varno, 0, parse->rtable,
 
258
                                                   subtlist, CMD_SELECT, 0);
 
259
 
 
260
                        foreach(rt, parse->rtable)
 
261
                        {
 
262
                                RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt);
 
263
 
 
264
                                if (otherrte->rtekind == RTE_JOIN)
 
265
                                        otherrte->joinaliasvars = (List *)
 
266
                                                ResolveNew((Node *) otherrte->joinaliasvars,
 
267
                                                                   varno, 0, parse->rtable,
 
268
                                                                   subtlist, CMD_SELECT, 0);
 
269
                        }
 
270
 
 
271
                        /*
 
272
                         * Now append the adjusted rtable entries to upper query. (We
 
273
                         * hold off until after fixing the upper rtable entries; no
 
274
                         * point in running that code on the subquery ones too.)
 
275
                         */
 
276
                        parse->rtable = list_concat(parse->rtable, subquery->rtable);
 
277
 
 
278
                        /*
 
279
                         * Pull up any FOR UPDATE markers, too.  (OffsetVarNodes
 
280
                         * already adjusted the marker values, so just list_concat the
 
281
                         * list.)
 
282
                         */
 
283
                        parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
 
284
 
 
285
                        /*
 
286
                         * We also have to fix the relid sets of any parent
 
287
                         * InClauseInfo nodes.  (This could perhaps be done by
 
288
                         * ResolveNew, but it would clutter that routine's API
 
289
                         * unreasonably.)
 
290
                         */
 
291
                        if (parse->in_info_list)
 
292
                        {
 
293
                                Relids          subrelids;
 
294
 
 
295
                                subrelids = get_relids_in_jointree((Node *) subquery->jointree);
 
296
                                fix_in_clause_relids(parse->in_info_list, varno, subrelids);
 
297
                        }
 
298
 
 
299
                        /*
 
300
                         * And now append any subquery InClauseInfos to our list.
 
301
                         */
 
302
                        parse->in_info_list = list_concat(parse->in_info_list,
 
303
                                                                                          subquery->in_info_list);
 
304
 
 
305
                        /*
 
306
                         * Miscellaneous housekeeping.
 
307
                         */
 
308
                        parse->hasSubLinks |= subquery->hasSubLinks;
 
309
                        /* subquery won't be pulled up if it hasAggs, so no work there */
 
310
 
 
311
                        /*
 
312
                         * Return the adjusted subquery jointree to replace the
 
313
                         * RangeTblRef entry in my jointree.
 
314
                         */
 
315
                        return (Node *) subquery->jointree;
 
316
                }
 
317
        }
 
318
        else if (IsA(jtnode, FromExpr))
 
319
        {
 
320
                FromExpr   *f = (FromExpr *) jtnode;
 
321
                ListCell   *l;
 
322
 
 
323
                foreach(l, f->fromlist)
 
324
                        lfirst(l) = pull_up_subqueries(parse, lfirst(l),
 
325
                                                                                   below_outer_join);
 
326
        }
 
327
        else if (IsA(jtnode, JoinExpr))
 
328
        {
 
329
                JoinExpr   *j = (JoinExpr *) jtnode;
 
330
 
 
331
                /* Recurse, being careful to tell myself when inside outer join */
 
332
                switch (j->jointype)
 
333
                {
 
334
                        case JOIN_INNER:
 
335
                                j->larg = pull_up_subqueries(parse, j->larg,
 
336
                                                                                         below_outer_join);
 
337
                                j->rarg = pull_up_subqueries(parse, j->rarg,
 
338
                                                                                         below_outer_join);
 
339
                                break;
 
340
                        case JOIN_LEFT:
 
341
                                j->larg = pull_up_subqueries(parse, j->larg,
 
342
                                                                                         below_outer_join);
 
343
                                j->rarg = pull_up_subqueries(parse, j->rarg,
 
344
                                                                                         true);
 
345
                                break;
 
346
                        case JOIN_FULL:
 
347
                                j->larg = pull_up_subqueries(parse, j->larg,
 
348
                                                                                         true);
 
349
                                j->rarg = pull_up_subqueries(parse, j->rarg,
 
350
                                                                                         true);
 
351
                                break;
 
352
                        case JOIN_RIGHT:
 
353
                                j->larg = pull_up_subqueries(parse, j->larg,
 
354
                                                                                         true);
 
355
                                j->rarg = pull_up_subqueries(parse, j->rarg,
 
356
                                                                                         below_outer_join);
 
357
                                break;
 
358
                        case JOIN_UNION:
 
359
 
 
360
                                /*
 
361
                                 * This is where we fail if upper levels of planner
 
362
                                 * haven't rewritten UNION JOIN as an Append ...
 
363
                                 */
 
364
                                ereport(ERROR,
 
365
                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
366
                                                 errmsg("UNION JOIN is not implemented")));
 
367
                                break;
 
368
                        default:
 
369
                                elog(ERROR, "unrecognized join type: %d",
 
370
                                         (int) j->jointype);
 
371
                                break;
 
372
                }
 
373
        }
 
374
        else
 
375
                elog(ERROR, "unrecognized node type: %d",
 
376
                         (int) nodeTag(jtnode));
 
377
        return jtnode;
 
378
}
 
379
 
 
380
/*
 
381
 * is_simple_subquery
 
382
 *        Check a subquery in the range table to see if it's simple enough
 
383
 *        to pull up into the parent query.
 
384
 */
 
385
static bool
 
386
is_simple_subquery(Query *subquery)
 
387
{
 
388
        /*
 
389
         * Let's just make sure it's a valid subselect ...
 
390
         */
 
391
        if (!IsA(subquery, Query) ||
 
392
                subquery->commandType != CMD_SELECT ||
 
393
                subquery->resultRelation != 0 ||
 
394
                subquery->into != NULL)
 
395
                elog(ERROR, "subquery is bogus");
 
396
 
 
397
        /*
 
398
         * Can't currently pull up a query with setops. Maybe after querytree
 
399
         * redesign...
 
400
         */
 
401
        if (subquery->setOperations)
 
402
                return false;
 
403
 
 
404
        /*
 
405
         * Can't pull up a subquery involving grouping, aggregation, sorting,
 
406
         * or limiting.
 
407
         */
 
408
        if (subquery->hasAggs ||
 
409
                subquery->groupClause ||
 
410
                subquery->havingQual ||
 
411
                subquery->sortClause ||
 
412
                subquery->distinctClause ||
 
413
                subquery->limitOffset ||
 
414
                subquery->limitCount)
 
415
                return false;
 
416
 
 
417
        /*
 
418
         * Don't pull up a subquery that has any set-returning functions in
 
419
         * its targetlist.      Otherwise we might well wind up inserting
 
420
         * set-returning functions into places where they mustn't go, such as
 
421
         * quals of higher queries.
 
422
         */
 
423
        if (expression_returns_set((Node *) subquery->targetList))
 
424
                return false;
 
425
 
 
426
        /*
 
427
         * Hack: don't try to pull up a subquery with an empty jointree.
 
428
         * query_planner() will correctly generate a Result plan for a
 
429
         * jointree that's totally empty, but I don't think the right things
 
430
         * happen if an empty FromExpr appears lower down in a jointree. Not
 
431
         * worth working hard on this, just to collapse SubqueryScan/Result
 
432
         * into Result...
 
433
         */
 
434
        if (subquery->jointree->fromlist == NIL)
 
435
                return false;
 
436
 
 
437
        return true;
 
438
}
 
439
 
 
440
/*
 
441
 * has_nullable_targetlist
 
442
 *        Check a subquery in the range table to see if all the non-junk
 
443
 *        targetlist items are simple variables or strict functions of simple
 
444
 *        variables (and, hence, will correctly go to NULL when examined above
 
445
 *        the point of an outer join).
 
446
 *
 
447
 * NOTE: it would be correct (and useful) to ignore output columns that aren't
 
448
 * actually referenced by the enclosing query ... but we do not have that
 
449
 * information available at this point.
 
450
 */
 
451
static bool
 
452
has_nullable_targetlist(Query *subquery)
 
453
{
 
454
        ListCell   *l;
 
455
 
 
456
        foreach(l, subquery->targetList)
 
457
        {
 
458
                TargetEntry *tle = (TargetEntry *) lfirst(l);
 
459
 
 
460
                /* ignore resjunk columns */
 
461
                if (tle->resdom->resjunk)
 
462
                        continue;
 
463
 
 
464
                /* Must contain a Var of current level */
 
465
                if (!contain_vars_of_level((Node *) tle->expr, 0))
 
466
                        return false;
 
467
 
 
468
                /* Must not contain any non-strict constructs */
 
469
                if (contain_nonstrict_functions((Node *) tle->expr))
 
470
                        return false;
 
471
 
 
472
                /* This one's OK, keep scanning */
 
473
        }
 
474
        return true;
 
475
}
 
476
 
 
477
/*
 
478
 * Helper routine for pull_up_subqueries: do ResolveNew on every expression
 
479
 * in the jointree, without changing the jointree structure itself.  Ugly,
 
480
 * but there's no other way...
 
481
 */
 
482
static void
 
483
resolvenew_in_jointree(Node *jtnode, int varno,
 
484
                                           List *rtable, List *subtlist)
 
485
{
 
486
        if (jtnode == NULL)
 
487
                return;
 
488
        if (IsA(jtnode, RangeTblRef))
 
489
        {
 
490
                /* nothing to do here */
 
491
        }
 
492
        else if (IsA(jtnode, FromExpr))
 
493
        {
 
494
                FromExpr   *f = (FromExpr *) jtnode;
 
495
                ListCell   *l;
 
496
 
 
497
                foreach(l, f->fromlist)
 
498
                        resolvenew_in_jointree(lfirst(l), varno, rtable, subtlist);
 
499
                f->quals = ResolveNew(f->quals,
 
500
                                                          varno, 0, rtable,
 
501
                                                          subtlist, CMD_SELECT, 0);
 
502
        }
 
503
        else if (IsA(jtnode, JoinExpr))
 
504
        {
 
505
                JoinExpr   *j = (JoinExpr *) jtnode;
 
506
 
 
507
                resolvenew_in_jointree(j->larg, varno, rtable, subtlist);
 
508
                resolvenew_in_jointree(j->rarg, varno, rtable, subtlist);
 
509
                j->quals = ResolveNew(j->quals,
 
510
                                                          varno, 0, rtable,
 
511
                                                          subtlist, CMD_SELECT, 0);
 
512
 
 
513
                /*
 
514
                 * We don't bother to update the colvars list, since it won't be
 
515
                 * used again ...
 
516
                 */
 
517
        }
 
518
        else
 
519
                elog(ERROR, "unrecognized node type: %d",
 
520
                         (int) nodeTag(jtnode));
 
521
}
 
522
 
 
523
/*
 
524
 * reduce_outer_joins
 
525
 *              Attempt to reduce outer joins to plain inner joins.
 
526
 *
 
527
 * The idea here is that given a query like
 
528
 *              SELECT ... FROM a LEFT JOIN b ON (...) WHERE b.y = 42;
 
529
 * we can reduce the LEFT JOIN to a plain JOIN if the "=" operator in WHERE
 
530
 * is strict.  The strict operator will always return NULL, causing the outer
 
531
 * WHERE to fail, on any row where the LEFT JOIN filled in NULLs for b's
 
532
 * columns.  Therefore, there's no need for the join to produce null-extended
 
533
 * rows in the first place --- which makes it a plain join not an outer join.
 
534
 * (This scenario may not be very likely in a query written out by hand, but
 
535
 * it's reasonably likely when pushing quals down into complex views.)
 
536
 *
 
537
 * More generally, an outer join can be reduced in strength if there is a
 
538
 * strict qual above it in the qual tree that constrains a Var from the
 
539
 * nullable side of the join to be non-null.  (For FULL joins this applies
 
540
 * to each side separately.)
 
541
 *
 
542
 * To ease recognition of strict qual clauses, we require this routine to be
 
543
 * run after expression preprocessing (i.e., qual canonicalization and JOIN
 
544
 * alias-var expansion).
 
545
 */
 
546
void
 
547
reduce_outer_joins(Query *parse)
 
548
{
 
549
        reduce_outer_joins_state *state;
 
550
 
 
551
        /*
 
552
         * To avoid doing strictness checks on more quals than necessary, we
 
553
         * want to stop descending the jointree as soon as there are no outer
 
554
         * joins below our current point.  This consideration forces a
 
555
         * two-pass process.  The first pass gathers information about which
 
556
         * base rels appear below each side of each join clause, and about
 
557
         * whether there are outer join(s) below each side of each join
 
558
         * clause. The second pass examines qual clauses and changes join
 
559
         * types as it descends the tree.
 
560
         */
 
561
        state = reduce_outer_joins_pass1((Node *) parse->jointree);
 
562
 
 
563
        /* planner.c shouldn't have called me if no outer joins */
 
564
        if (state == NULL || !state->contains_outer)
 
565
                elog(ERROR, "so where are the outer joins?");
 
566
 
 
567
        reduce_outer_joins_pass2((Node *) parse->jointree, state, parse, NULL);
 
568
}
 
569
 
 
570
/*
 
571
 * reduce_outer_joins_pass1 - phase 1 data collection
 
572
 *
 
573
 * Returns a state node describing the given jointree node.
 
574
 */
 
575
static reduce_outer_joins_state *
 
576
reduce_outer_joins_pass1(Node *jtnode)
 
577
{
 
578
        reduce_outer_joins_state *result;
 
579
 
 
580
        result = (reduce_outer_joins_state *)
 
581
                palloc(sizeof(reduce_outer_joins_state));
 
582
        result->relids = NULL;
 
583
        result->contains_outer = false;
 
584
        result->sub_states = NIL;
 
585
 
 
586
        if (jtnode == NULL)
 
587
                return result;
 
588
        if (IsA(jtnode, RangeTblRef))
 
589
        {
 
590
                int                     varno = ((RangeTblRef *) jtnode)->rtindex;
 
591
 
 
592
                result->relids = bms_make_singleton(varno);
 
593
        }
 
594
        else if (IsA(jtnode, FromExpr))
 
595
        {
 
596
                FromExpr   *f = (FromExpr *) jtnode;
 
597
                ListCell   *l;
 
598
 
 
599
                foreach(l, f->fromlist)
 
600
                {
 
601
                        reduce_outer_joins_state *sub_state;
 
602
 
 
603
                        sub_state = reduce_outer_joins_pass1(lfirst(l));
 
604
                        result->relids = bms_add_members(result->relids,
 
605
                                                                                         sub_state->relids);
 
606
                        result->contains_outer |= sub_state->contains_outer;
 
607
                        result->sub_states = lappend(result->sub_states, sub_state);
 
608
                }
 
609
        }
 
610
        else if (IsA(jtnode, JoinExpr))
 
611
        {
 
612
                JoinExpr   *j = (JoinExpr *) jtnode;
 
613
                reduce_outer_joins_state *sub_state;
 
614
 
 
615
                /* join's own RT index is not wanted in result->relids */
 
616
                if (IS_OUTER_JOIN(j->jointype))
 
617
                        result->contains_outer = true;
 
618
 
 
619
                sub_state = reduce_outer_joins_pass1(j->larg);
 
620
                result->relids = bms_add_members(result->relids,
 
621
                                                                                 sub_state->relids);
 
622
                result->contains_outer |= sub_state->contains_outer;
 
623
                result->sub_states = lappend(result->sub_states, sub_state);
 
624
 
 
625
                sub_state = reduce_outer_joins_pass1(j->rarg);
 
626
                result->relids = bms_add_members(result->relids,
 
627
                                                                                 sub_state->relids);
 
628
                result->contains_outer |= sub_state->contains_outer;
 
629
                result->sub_states = lappend(result->sub_states, sub_state);
 
630
        }
 
631
        else
 
632
                elog(ERROR, "unrecognized node type: %d",
 
633
                         (int) nodeTag(jtnode));
 
634
        return result;
 
635
}
 
636
 
 
637
/*
 
638
 * reduce_outer_joins_pass2 - phase 2 processing
 
639
 *
 
640
 *      jtnode: current jointree node
 
641
 *      state: state data collected by phase 1 for this node
 
642
 *      parse: toplevel Query
 
643
 *      nonnullable_rels: set of base relids forced non-null by upper quals
 
644
 */
 
645
static void
 
646
reduce_outer_joins_pass2(Node *jtnode,
 
647
                                                 reduce_outer_joins_state *state,
 
648
                                                 Query *parse,
 
649
                                                 Relids nonnullable_rels)
 
650
{
 
651
        /*
 
652
         * pass 2 should never descend as far as an empty subnode or base rel,
 
653
         * because it's only called on subtrees marked as contains_outer.
 
654
         */
 
655
        if (jtnode == NULL)
 
656
                elog(ERROR, "reached empty jointree");
 
657
        if (IsA(jtnode, RangeTblRef))
 
658
                elog(ERROR, "reached base rel");
 
659
        else if (IsA(jtnode, FromExpr))
 
660
        {
 
661
                FromExpr   *f = (FromExpr *) jtnode;
 
662
                ListCell   *l;
 
663
                ListCell   *s;
 
664
                Relids          pass_nonnullable;
 
665
 
 
666
                /* Scan quals to see if we can add any nonnullability constraints */
 
667
                pass_nonnullable = find_nonnullable_rels(f->quals, true);
 
668
                pass_nonnullable = bms_add_members(pass_nonnullable,
 
669
                                                                                   nonnullable_rels);
 
670
                /* And recurse --- but only into interesting subtrees */
 
671
                Assert(list_length(f->fromlist) == list_length(state->sub_states));
 
672
                forboth(l, f->fromlist, s, state->sub_states)
 
673
                {
 
674
                        reduce_outer_joins_state *sub_state = lfirst(s);
 
675
 
 
676
                        if (sub_state->contains_outer)
 
677
                                reduce_outer_joins_pass2(lfirst(l), sub_state, parse,
 
678
                                                                                 pass_nonnullable);
 
679
                }
 
680
                bms_free(pass_nonnullable);
 
681
        }
 
682
        else if (IsA(jtnode, JoinExpr))
 
683
        {
 
684
                JoinExpr   *j = (JoinExpr *) jtnode;
 
685
                int                     rtindex = j->rtindex;
 
686
                JoinType        jointype = j->jointype;
 
687
                reduce_outer_joins_state *left_state = linitial(state->sub_states);
 
688
                reduce_outer_joins_state *right_state = lsecond(state->sub_states);
 
689
 
 
690
                /* Can we simplify this join? */
 
691
                switch (jointype)
 
692
                {
 
693
                        case JOIN_LEFT:
 
694
                                if (bms_overlap(nonnullable_rels, right_state->relids))
 
695
                                        jointype = JOIN_INNER;
 
696
                                break;
 
697
                        case JOIN_RIGHT:
 
698
                                if (bms_overlap(nonnullable_rels, left_state->relids))
 
699
                                        jointype = JOIN_INNER;
 
700
                                break;
 
701
                        case JOIN_FULL:
 
702
                                if (bms_overlap(nonnullable_rels, left_state->relids))
 
703
                                {
 
704
                                        if (bms_overlap(nonnullable_rels, right_state->relids))
 
705
                                                jointype = JOIN_INNER;
 
706
                                        else
 
707
                                                jointype = JOIN_LEFT;
 
708
                                }
 
709
                                else
 
710
                                {
 
711
                                        if (bms_overlap(nonnullable_rels, right_state->relids))
 
712
                                                jointype = JOIN_RIGHT;
 
713
                                }
 
714
                                break;
 
715
                        default:
 
716
                                break;
 
717
                }
 
718
                if (jointype != j->jointype)
 
719
                {
 
720
                        /* apply the change to both jointree node and RTE */
 
721
                        RangeTblEntry *rte = rt_fetch(rtindex, parse->rtable);
 
722
 
 
723
                        Assert(rte->rtekind == RTE_JOIN);
 
724
                        Assert(rte->jointype == j->jointype);
 
725
                        rte->jointype = j->jointype = jointype;
 
726
                }
 
727
 
 
728
                /* Only recurse if there's more to do below here */
 
729
                if (left_state->contains_outer || right_state->contains_outer)
 
730
                {
 
731
                        Relids          local_nonnullable;
 
732
                        Relids          pass_nonnullable;
 
733
 
 
734
                        /*
 
735
                         * If this join is (now) inner, we can add any nonnullability
 
736
                         * constraints its quals provide to those we got from above.
 
737
                         * But if it is outer, we can only pass down the local
 
738
                         * constraints into the nullable side, because an outer join
 
739
                         * never eliminates any rows from its non-nullable side.  If
 
740
                         * it's a FULL join then it doesn't eliminate anything from
 
741
                         * either side.
 
742
                         */
 
743
                        if (jointype != JOIN_FULL)
 
744
                        {
 
745
                                local_nonnullable = find_nonnullable_rels(j->quals, true);
 
746
                                local_nonnullable = bms_add_members(local_nonnullable,
 
747
                                                                                                        nonnullable_rels);
 
748
                        }
 
749
                        else
 
750
                                local_nonnullable = NULL;               /* no use in calculating
 
751
                                                                                                 * it */
 
752
 
 
753
                        if (left_state->contains_outer)
 
754
                        {
 
755
                                if (jointype == JOIN_INNER || jointype == JOIN_RIGHT)
 
756
                                        pass_nonnullable = local_nonnullable;
 
757
                                else
 
758
                                        pass_nonnullable = nonnullable_rels;
 
759
                                reduce_outer_joins_pass2(j->larg, left_state, parse,
 
760
                                                                                 pass_nonnullable);
 
761
                        }
 
762
                        if (right_state->contains_outer)
 
763
                        {
 
764
                                if (jointype == JOIN_INNER || jointype == JOIN_LEFT)
 
765
                                        pass_nonnullable = local_nonnullable;
 
766
                                else
 
767
                                        pass_nonnullable = nonnullable_rels;
 
768
                                reduce_outer_joins_pass2(j->rarg, right_state, parse,
 
769
                                                                                 pass_nonnullable);
 
770
                        }
 
771
                        bms_free(local_nonnullable);
 
772
                }
 
773
        }
 
774
        else
 
775
                elog(ERROR, "unrecognized node type: %d",
 
776
                         (int) nodeTag(jtnode));
 
777
}
 
778
 
 
779
/*
 
780
 * find_nonnullable_rels
 
781
 *              Determine which base rels are forced nonnullable by given quals
 
782
 *
 
783
 * We don't use expression_tree_walker here because we don't want to
 
784
 * descend through very many kinds of nodes; only the ones we can be sure
 
785
 * are strict.  We can descend through the top level of implicit AND'ing,
 
786
 * but not through any explicit ANDs (or ORs) below that, since those are not
 
787
 * strict constructs.  The List case handles the top-level implicit AND list
 
788
 * as well as lists of arguments to strict operators/functions.
 
789
 */
 
790
static Relids
 
791
find_nonnullable_rels(Node *node, bool top_level)
 
792
{
 
793
        Relids          result = NULL;
 
794
 
 
795
        if (node == NULL)
 
796
                return NULL;
 
797
        if (IsA(node, Var))
 
798
        {
 
799
                Var                *var = (Var *) node;
 
800
 
 
801
                if (var->varlevelsup == 0)
 
802
                        result = bms_make_singleton(var->varno);
 
803
        }
 
804
        else if (IsA(node, List))
 
805
        {
 
806
                ListCell   *l;
 
807
 
 
808
                foreach(l, (List *) node)
 
809
                {
 
810
                        result = bms_join(result, find_nonnullable_rels(lfirst(l),
 
811
                                                                                                                        top_level));
 
812
                }
 
813
        }
 
814
        else if (IsA(node, FuncExpr))
 
815
        {
 
816
                FuncExpr   *expr = (FuncExpr *) node;
 
817
 
 
818
                if (func_strict(expr->funcid))
 
819
                        result = find_nonnullable_rels((Node *) expr->args, false);
 
820
        }
 
821
        else if (IsA(node, OpExpr))
 
822
        {
 
823
                OpExpr     *expr = (OpExpr *) node;
 
824
 
 
825
                if (op_strict(expr->opno))
 
826
                        result = find_nonnullable_rels((Node *) expr->args, false);
 
827
        }
 
828
        else if (IsA(node, BoolExpr))
 
829
        {
 
830
                BoolExpr   *expr = (BoolExpr *) node;
 
831
 
 
832
                /* NOT is strict, others are not */
 
833
                if (expr->boolop == NOT_EXPR)
 
834
                        result = find_nonnullable_rels((Node *) expr->args, false);
 
835
        }
 
836
        else if (IsA(node, RelabelType))
 
837
        {
 
838
                RelabelType *expr = (RelabelType *) node;
 
839
 
 
840
                result = find_nonnullable_rels((Node *) expr->arg, top_level);
 
841
        }
 
842
        else if (IsA(node, ConvertRowtypeExpr))
 
843
        {
 
844
                /* not clear this is useful, but it can't hurt */
 
845
                ConvertRowtypeExpr *expr = (ConvertRowtypeExpr *) node;
 
846
 
 
847
                result = find_nonnullable_rels((Node *) expr->arg, top_level);
 
848
        }
 
849
        else if (IsA(node, NullTest))
 
850
        {
 
851
                NullTest   *expr = (NullTest *) node;
 
852
 
 
853
                /*
 
854
                 * IS NOT NULL can be considered strict, but only at top level;
 
855
                 * else we might have something like NOT (x IS NOT NULL).
 
856
                 */
 
857
                if (top_level && expr->nulltesttype == IS_NOT_NULL)
 
858
                        result = find_nonnullable_rels((Node *) expr->arg, false);
 
859
        }
 
860
        else if (IsA(node, BooleanTest))
 
861
        {
 
862
                BooleanTest *expr = (BooleanTest *) node;
 
863
 
 
864
                /*
 
865
                 * Appropriate boolean tests are strict at top level.
 
866
                 */
 
867
                if (top_level &&
 
868
                        (expr->booltesttype == IS_TRUE ||
 
869
                         expr->booltesttype == IS_FALSE ||
 
870
                         expr->booltesttype == IS_NOT_UNKNOWN))
 
871
                        result = find_nonnullable_rels((Node *) expr->arg, false);
 
872
        }
 
873
        return result;
 
874
}
 
875
 
 
876
/*
 
877
 * simplify_jointree
 
878
 *              Attempt to simplify a query's jointree.
 
879
 *
 
880
 * If we succeed in pulling up a subquery then we might form a jointree
 
881
 * in which a FromExpr is a direct child of another FromExpr.  In that
 
882
 * case we can consider collapsing the two FromExprs into one.  This is
 
883
 * an optional conversion, since the planner will work correctly either
 
884
 * way.  But we may find a better plan (at the cost of more planning time)
 
885
 * if we merge the two nodes, creating a single join search space out of
 
886
 * two.  To allow the user to trade off planning time against plan quality,
 
887
 * we provide a control parameter from_collapse_limit that limits the size
 
888
 * of the join search space that can be created this way.
 
889
 *
 
890
 * We also consider flattening explicit inner JOINs into FromExprs (which
 
891
 * will in turn allow them to be merged into parent FromExprs).  The tradeoffs
 
892
 * here are the same as for flattening FromExprs, but we use a different
 
893
 * control parameter so that the user can use explicit JOINs to control the
 
894
 * join order even when they are inner JOINs.
 
895
 *
 
896
 * NOTE: don't try to do this in the same jointree scan that does subquery
 
897
 * pullup!      Since we're changing the jointree structure here, that wouldn't
 
898
 * work reliably --- see comments for pull_up_subqueries().
 
899
 */
 
900
Node *
 
901
simplify_jointree(Query *parse, Node *jtnode)
 
902
{
 
903
        if (jtnode == NULL)
 
904
                return NULL;
 
905
        if (IsA(jtnode, RangeTblRef))
 
906
        {
 
907
                /* nothing to do here... */
 
908
        }
 
909
        else if (IsA(jtnode, FromExpr))
 
910
        {
 
911
                FromExpr   *f = (FromExpr *) jtnode;
 
912
                List       *newlist = NIL;
 
913
                int                     children_remaining;
 
914
                ListCell   *l;
 
915
 
 
916
                children_remaining = list_length(f->fromlist);
 
917
                foreach(l, f->fromlist)
 
918
                {
 
919
                        Node       *child = (Node *) lfirst(l);
 
920
 
 
921
                        children_remaining--;
 
922
                        /* Recursively simplify this child... */
 
923
                        child = simplify_jointree(parse, child);
 
924
                        /* Now, is it a FromExpr? */
 
925
                        if (child && IsA(child, FromExpr))
 
926
                        {
 
927
                                /*
 
928
                                 * Yes, so do we want to merge it into parent?  Always do
 
929
                                 * so if child has just one element (since that doesn't
 
930
                                 * make the parent's list any longer).  Otherwise merge if
 
931
                                 * the resulting join list would be no longer than
 
932
                                 * from_collapse_limit.
 
933
                                 */
 
934
                                FromExpr   *subf = (FromExpr *) child;
 
935
                                int                     childlen = list_length(subf->fromlist);
 
936
                                int                     myothers = list_length(newlist) + children_remaining;
 
937
 
 
938
                                if (childlen <= 1 ||
 
939
                                        (childlen + myothers) <= from_collapse_limit)
 
940
                                {
 
941
                                        newlist = list_concat(newlist, subf->fromlist);
 
942
 
 
943
                                        /*
 
944
                                         * By now, the quals have been converted to
 
945
                                         * implicit-AND lists, so we just need to join the
 
946
                                         * lists.  NOTE: we put the pulled-up quals first.
 
947
                                         */
 
948
                                        f->quals = (Node *) list_concat((List *) subf->quals,
 
949
                                                                                                        (List *) f->quals);
 
950
                                }
 
951
                                else
 
952
                                        newlist = lappend(newlist, child);
 
953
                        }
 
954
                        else
 
955
                                newlist = lappend(newlist, child);
 
956
                }
 
957
                f->fromlist = newlist;
 
958
        }
 
959
        else if (IsA(jtnode, JoinExpr))
 
960
        {
 
961
                JoinExpr   *j = (JoinExpr *) jtnode;
 
962
 
 
963
                /* Recursively simplify the children... */
 
964
                j->larg = simplify_jointree(parse, j->larg);
 
965
                j->rarg = simplify_jointree(parse, j->rarg);
 
966
 
 
967
                /*
 
968
                 * If it is an outer join, we must not flatten it.      An inner join
 
969
                 * is semantically equivalent to a FromExpr; we convert it to one,
 
970
                 * allowing it to be flattened into its parent, if the resulting
 
971
                 * FromExpr would have no more than join_collapse_limit members.
 
972
                 */
 
973
                if (j->jointype == JOIN_INNER && join_collapse_limit > 1)
 
974
                {
 
975
                        int                     leftlen,
 
976
                                                rightlen;
 
977
 
 
978
                        if (j->larg && IsA(j->larg, FromExpr))
 
979
                                leftlen = list_length(((FromExpr *) j->larg)->fromlist);
 
980
                        else
 
981
                                leftlen = 1;
 
982
                        if (j->rarg && IsA(j->rarg, FromExpr))
 
983
                                rightlen = list_length(((FromExpr *) j->rarg)->fromlist);
 
984
                        else
 
985
                                rightlen = 1;
 
986
                        if ((leftlen + rightlen) <= join_collapse_limit)
 
987
                        {
 
988
                                FromExpr   *f = makeNode(FromExpr);
 
989
 
 
990
                                f->fromlist = NIL;
 
991
                                f->quals = NULL;
 
992
 
 
993
                                if (j->larg && IsA(j->larg, FromExpr))
 
994
                                {
 
995
                                        FromExpr   *subf = (FromExpr *) j->larg;
 
996
 
 
997
                                        f->fromlist = subf->fromlist;
 
998
                                        f->quals = subf->quals;
 
999
                                }
 
1000
                                else
 
1001
                                        f->fromlist = list_make1(j->larg);
 
1002
 
 
1003
                                if (j->rarg && IsA(j->rarg, FromExpr))
 
1004
                                {
 
1005
                                        FromExpr   *subf = (FromExpr *) j->rarg;
 
1006
 
 
1007
                                        f->fromlist = list_concat(f->fromlist,
 
1008
                                                                                          subf->fromlist);
 
1009
                                        f->quals = (Node *) list_concat((List *) f->quals,
 
1010
                                                                                                        (List *) subf->quals);
 
1011
                                }
 
1012
                                else
 
1013
                                        f->fromlist = lappend(f->fromlist, j->rarg);
 
1014
 
 
1015
                                /* pulled-up quals first */
 
1016
                                f->quals = (Node *) list_concat((List *) f->quals,
 
1017
                                                                                                (List *) j->quals);
 
1018
 
 
1019
                                return (Node *) f;
 
1020
                        }
 
1021
                }
 
1022
        }
 
1023
        else
 
1024
                elog(ERROR, "unrecognized node type: %d",
 
1025
                         (int) nodeTag(jtnode));
 
1026
        return jtnode;
 
1027
}
 
1028
 
 
1029
/*
 
1030
 * fix_in_clause_relids: update RT-index sets of InClauseInfo nodes
 
1031
 *
 
1032
 * When we pull up a subquery, any InClauseInfo references to the subquery's
 
1033
 * RT index have to be replaced by the set of substituted relids.
 
1034
 *
 
1035
 * We assume we may modify the InClauseInfo nodes in-place.
 
1036
 */
 
1037
static void
 
1038
fix_in_clause_relids(List *in_info_list, int varno, Relids subrelids)
 
1039
{
 
1040
        ListCell   *l;
 
1041
 
 
1042
        foreach(l, in_info_list)
 
1043
        {
 
1044
                InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
 
1045
 
 
1046
                if (bms_is_member(varno, ininfo->lefthand))
 
1047
                {
 
1048
                        ininfo->lefthand = bms_del_member(ininfo->lefthand, varno);
 
1049
                        ininfo->lefthand = bms_add_members(ininfo->lefthand, subrelids);
 
1050
                }
 
1051
                if (bms_is_member(varno, ininfo->righthand))
 
1052
                {
 
1053
                        ininfo->righthand = bms_del_member(ininfo->righthand, varno);
 
1054
                        ininfo->righthand = bms_add_members(ininfo->righthand, subrelids);
 
1055
                }
 
1056
        }
 
1057
}
 
1058
 
 
1059
/*
 
1060
 * get_relids_in_jointree: get set of base RT indexes present in a jointree
 
1061
 */
 
1062
Relids
 
1063
get_relids_in_jointree(Node *jtnode)
 
1064
{
 
1065
        Relids          result = NULL;
 
1066
 
 
1067
        if (jtnode == NULL)
 
1068
                return result;
 
1069
        if (IsA(jtnode, RangeTblRef))
 
1070
        {
 
1071
                int                     varno = ((RangeTblRef *) jtnode)->rtindex;
 
1072
 
 
1073
                result = bms_make_singleton(varno);
 
1074
        }
 
1075
        else if (IsA(jtnode, FromExpr))
 
1076
        {
 
1077
                FromExpr   *f = (FromExpr *) jtnode;
 
1078
                ListCell   *l;
 
1079
 
 
1080
                foreach(l, f->fromlist)
 
1081
                {
 
1082
                        result = bms_join(result,
 
1083
                                                          get_relids_in_jointree(lfirst(l)));
 
1084
                }
 
1085
        }
 
1086
        else if (IsA(jtnode, JoinExpr))
 
1087
        {
 
1088
                JoinExpr   *j = (JoinExpr *) jtnode;
 
1089
 
 
1090
                /* join's own RT index is not wanted in result */
 
1091
                result = get_relids_in_jointree(j->larg);
 
1092
                result = bms_join(result, get_relids_in_jointree(j->rarg));
 
1093
        }
 
1094
        else
 
1095
                elog(ERROR, "unrecognized node type: %d",
 
1096
                         (int) nodeTag(jtnode));
 
1097
        return result;
 
1098
}
 
1099
 
 
1100
/*
 
1101
 * get_relids_for_join: get set of base RT indexes making up a join
 
1102
 *
 
1103
 * NB: this will not work reliably after simplify_jointree() is run,
 
1104
 * since that may eliminate join nodes from the jointree.
 
1105
 */
 
1106
Relids
 
1107
get_relids_for_join(Query *parse, int joinrelid)
 
1108
{
 
1109
        Node       *jtnode;
 
1110
 
 
1111
        jtnode = find_jointree_node_for_rel((Node *) parse->jointree, joinrelid);
 
1112
        if (!jtnode)
 
1113
                elog(ERROR, "could not find join node %d", joinrelid);
 
1114
        return get_relids_in_jointree(jtnode);
 
1115
}
 
1116
 
 
1117
/*
 
1118
 * find_jointree_node_for_rel: locate jointree node for a base or join RT index
 
1119
 *
 
1120
 * Returns NULL if not found
 
1121
 */
 
1122
static Node *
 
1123
find_jointree_node_for_rel(Node *jtnode, int relid)
 
1124
{
 
1125
        if (jtnode == NULL)
 
1126
                return NULL;
 
1127
        if (IsA(jtnode, RangeTblRef))
 
1128
        {
 
1129
                int                     varno = ((RangeTblRef *) jtnode)->rtindex;
 
1130
 
 
1131
                if (relid == varno)
 
1132
                        return jtnode;
 
1133
        }
 
1134
        else if (IsA(jtnode, FromExpr))
 
1135
        {
 
1136
                FromExpr   *f = (FromExpr *) jtnode;
 
1137
                ListCell   *l;
 
1138
 
 
1139
                foreach(l, f->fromlist)
 
1140
                {
 
1141
                        jtnode = find_jointree_node_for_rel(lfirst(l), relid);
 
1142
                        if (jtnode)
 
1143
                                return jtnode;
 
1144
                }
 
1145
        }
 
1146
        else if (IsA(jtnode, JoinExpr))
 
1147
        {
 
1148
                JoinExpr   *j = (JoinExpr *) jtnode;
 
1149
 
 
1150
                if (relid == j->rtindex)
 
1151
                        return jtnode;
 
1152
                jtnode = find_jointree_node_for_rel(j->larg, relid);
 
1153
                if (jtnode)
 
1154
                        return jtnode;
 
1155
                jtnode = find_jointree_node_for_rel(j->rarg, relid);
 
1156
                if (jtnode)
 
1157
                        return jtnode;
 
1158
        }
 
1159
        else
 
1160
                elog(ERROR, "unrecognized node type: %d",
 
1161
                         (int) nodeTag(jtnode));
 
1162
        return NULL;
 
1163
}