summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path/joinrels.c
blob: 929a977112d13e515b81f12f5aee1d781b79038c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
/*-------------------------------------------------------------------------
 *
 * joinrels.c
 *	  Routines to determine which relations should be joined
 *
 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.52 2001/03/22 03:59:35 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "optimizer/pathnode.h"
#include "optimizer/paths.h"


static RelOptInfo *make_join_rel(Query *root, RelOptInfo *rel1,
			  RelOptInfo *rel2, JoinType jointype);


/*
 * make_rels_by_joins
 *	  Consider ways to produce join relations containing exactly 'level'
 *	  jointree items.  (This is one step of the dynamic-programming method
 *	  embodied in make_one_rel_by_joins.)  Join rel nodes for each feasible
 *	  combination of lower-level rels are created and returned in a list.
 *	  Implementation paths are created for each such joinrel, too.
 *
 * level: level of rels we want to make this time.
 * joinrels[j], 1 <= j < level, is a list of rels containing j items.
 */
List *
make_rels_by_joins(Query *root, int level, List **joinrels)
{
	List	   *result_rels = NIL;
	List	   *new_rels;
	List	   *nr;
	List	   *r;
	int			k;

	/*
	 * First, consider left-sided and right-sided plans, in which rels of
	 * exactly level-1 member relations are joined against initial
	 * relations. We prefer to join using join clauses, but if we find a
	 * rel of level-1 members that has no join clauses, we will generate
	 * Cartesian-product joins against all initial rels not already
	 * contained in it.
	 *
	 * In the first pass (level == 2), we try to join each initial rel to
	 * each initial rel that appears later in joinrels[1].	(The
	 * mirror-image joins are handled automatically by make_join_rel.)	In
	 * later passes, we try to join rels of size level-1 from
	 * joinrels[level-1] to each initial rel in joinrels[1].
	 */
	foreach(r, joinrels[level - 1])
	{
		RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
		List	   *other_rels;

		if (level == 2)
			other_rels = lnext(r);		/* only consider remaining initial
										 * rels */
		else
			other_rels = joinrels[1];	/* consider all initial rels */

		if (old_rel->joininfo != NIL)
		{

			/*
			 * Note that if all available join clauses for this rel
			 * require more than one other rel, we will fail to make any
			 * joins against it here.  That's OK; it'll be considered by
			 * "bushy plan" join code in a higher-level pass where we have
			 * those other rels collected into a join rel.	See also the
			 * last-ditch case below.
			 */
			new_rels = make_rels_by_clause_joins(root,
												 old_rel,
												 other_rels);
		}
		else
		{

			/*
			 * Oops, we have a relation that is not joined to any other
			 * relation.  Cartesian product time.
			 */
			new_rels = make_rels_by_clauseless_joins(root,
													 old_rel,
													 other_rels);
		}

		/*
		 * At levels above 2 we will generate the same joined relation in
		 * multiple ways --- for example (a join b) join c is the same
		 * RelOptInfo as (b join c) join a, though the second case will
		 * add a different set of Paths to it.	To avoid making extra work
		 * for subsequent passes, do not enter the same RelOptInfo into
		 * our output list multiple times.
		 */
		foreach(nr, new_rels)
		{
			RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);

			if (!ptrMember(jrel, result_rels))
				result_rels = lcons(jrel, result_rels);
		}
	}

	/*
	 * Now, consider "bushy plans" in which relations of k initial rels
	 * are joined to relations of level-k initial rels, for 2 <= k <=
	 * level-2.
	 *
	 * We only consider bushy-plan joins for pairs of rels where there is a
	 * suitable join clause, in order to avoid unreasonable growth of
	 * planning time.
	 */
	for (k = 2;; k++)
	{
		int			other_level = level - k;

		/*
		 * Since make_join_rel(x, y) handles both x,y and y,x cases, we
		 * only need to go as far as the halfway point.
		 */
		if (k > other_level)
			break;

		foreach(r, joinrels[k])
		{
			RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
			List	   *other_rels;
			List	   *r2;

			if (old_rel->joininfo == NIL)
				continue;		/* we ignore clauseless joins here */

			if (k == other_level)
				other_rels = lnext(r);	/* only consider remaining rels */
			else
				other_rels = joinrels[other_level];

			foreach(r2, other_rels)
			{
				RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);

				if (nonoverlap_setsi(old_rel->relids, new_rel->relids))
				{
					List	   *i;

					/*
					 * OK, we can build a rel of the right level from this
					 * pair of rels.  Do so if there is at least one
					 * usable join clause.
					 */
					foreach(i, old_rel->joininfo)
					{
						JoinInfo   *joininfo = (JoinInfo *) lfirst(i);

						if (is_subseti(joininfo->unjoined_relids,
									   new_rel->relids))
						{
							RelOptInfo *jrel;

							jrel = make_join_rel(root, old_rel, new_rel,
												 JOIN_INNER);
							/* Avoid making duplicate entries ... */
							if (!ptrMember(jrel, result_rels))
								result_rels = lcons(jrel, result_rels);
							break;		/* need not consider more
										 * joininfos */
						}
					}
				}
			}
		}
	}

	/*
	 * Last-ditch effort: if we failed to find any usable joins so far,
	 * force a set of cartesian-product joins to be generated.	This
	 * handles the special case where all the available rels have join
	 * clauses but we cannot use any of the joins yet.	An example is
	 *
	 * SELECT * FROM a,b,c WHERE (a.f1 + b.f2 + c.f3) = 0;
	 *
	 * The join clause will be usable at level 3, but at level 2 we have no
	 * choice but to make cartesian joins.	We consider only left-sided
	 * and right-sided cartesian joins in this case (no bushy).
	 */
	if (result_rels == NIL)
	{

		/*
		 * This loop is just like the first one, except we always call
		 * make_rels_by_clauseless_joins().
		 */
		foreach(r, joinrels[level - 1])
		{
			RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
			List	   *other_rels;

			if (level == 2)
				other_rels = lnext(r);	/* only consider remaining initial
										 * rels */
			else
				other_rels = joinrels[1];		/* consider all initial
												 * rels */

			new_rels = make_rels_by_clauseless_joins(root,
													 old_rel,
													 other_rels);

			foreach(nr, new_rels)
			{
				RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);

				if (!ptrMember(jrel, result_rels))
					result_rels = lcons(jrel, result_rels);
			}
		}

		if (result_rels == NIL)
			elog(ERROR, "make_rels_by_joins: failed to build any %d-way joins",
				 level);
	}

	return result_rels;
}

/*
 * make_rels_by_clause_joins
 *	  Build joins between the given relation 'old_rel' and other relations
 *	  that are mentioned within old_rel's joininfo nodes (i.e., relations
 *	  that participate in join clauses that 'old_rel' also participates in).
 *	  The join rel nodes are returned in a list.
 *
 * 'old_rel' is the relation entry for the relation to be joined
 * 'other_rels': other rels to be considered for joining
 *
 * Currently, this is only used with initial rels in other_rels, but it
 * will work for joining to joinrels too, if the caller ensures there is no
 * membership overlap between old_rel and the rels in other_rels.  (We need
 * no extra test for overlap for initial rels, since the is_subset test can
 * only succeed when other_rel is not already part of old_rel.)
 */
List *
make_rels_by_clause_joins(Query *root,
						  RelOptInfo *old_rel,
						  List *other_rels)
{
	List	   *result = NIL;
	List	   *i,
			   *j;

	foreach(i, old_rel->joininfo)
	{
		JoinInfo   *joininfo = (JoinInfo *) lfirst(i);
		Relids		unjoined_relids = joininfo->unjoined_relids;

		foreach(j, other_rels)
		{
			RelOptInfo *other_rel = (RelOptInfo *) lfirst(j);

			if (is_subseti(unjoined_relids, other_rel->relids))
			{
				RelOptInfo *jrel;

				jrel = make_join_rel(root, old_rel, other_rel, JOIN_INNER);

				/*
				 * Avoid entering same joinrel into our output list more
				 * than once.  (make_rels_by_joins doesn't really care,
				 * but GEQO does.)
				 */
				if (!ptrMember(jrel, result))
					result = lcons(jrel, result);
			}
		}
	}

	return result;
}

/*
 * make_rels_by_clauseless_joins
 *	  Given a relation 'old_rel' and a list of other relations
 *	  'other_rels', create a join relation between 'old_rel' and each
 *	  member of 'other_rels' that isn't already included in 'old_rel'.
 *	  The join rel nodes are returned in a list.
 *
 * 'old_rel' is the relation entry for the relation to be joined
 * 'other_rels': other rels to be considered for joining
 *
 * Currently, this is only used with initial rels in other_rels, but it would
 * work for joining to joinrels too.
 */
List *
make_rels_by_clauseless_joins(Query *root,
							  RelOptInfo *old_rel,
							  List *other_rels)
{
	List	   *result = NIL;
	List	   *i;

	foreach(i, other_rels)
	{
		RelOptInfo *other_rel = (RelOptInfo *) lfirst(i);

		if (nonoverlap_setsi(other_rel->relids, old_rel->relids))
		{
			RelOptInfo *jrel;

			jrel = make_join_rel(root, old_rel, other_rel, JOIN_INNER);

			/*
			 * As long as given other_rels are distinct, don't need to
			 * test to see if jrel is already part of output list.
			 */
			result = lcons(jrel, result);
		}
	}

	return result;
}


/*
 * make_jointree_rel
 *		Find or build a RelOptInfojoin rel representing a specific
 *		jointree item.	For JoinExprs, we only consider the construction
 *		path that corresponds exactly to what the user wrote.
 */
RelOptInfo *
make_jointree_rel(Query *root, Node *jtnode)
{
	if (IsA(jtnode, RangeTblRef))
	{
		int			varno = ((RangeTblRef *) jtnode)->rtindex;

		return get_base_rel(root, varno);
	}
	else if (IsA(jtnode, FromExpr))
	{
		FromExpr   *f = (FromExpr *) jtnode;

		/* Recurse back to multi-way-join planner */
		return make_fromexpr_rel(root, f);
	}
	else if (IsA(jtnode, JoinExpr))
	{
		JoinExpr   *j = (JoinExpr *) jtnode;
		RelOptInfo *rel,
				   *lrel,
				   *rrel;

		/* Recurse */
		lrel = make_jointree_rel(root, j->larg);
		rrel = make_jointree_rel(root, j->rarg);

		/* Make this join rel */
		rel = make_join_rel(root, lrel, rrel, j->jointype);

		/*
		 * Since we are only going to consider this one way to do it,
		 * we're done generating Paths for this joinrel and can now select
		 * the cheapest.  In fact we *must* do so now, since next level up
		 * will need it!
		 */
		set_cheapest(rel);

		return rel;
	}
	else
		elog(ERROR, "make_jointree_rel: unexpected node type %d",
			 nodeTag(jtnode));
	return NULL;				/* keep compiler quiet */
}


/*
 * make_join_rel
 *	   Find or create a join RelOptInfo that represents the join of
 *	   the two given rels, and add to it path information for paths
 *	   created with the two rels as outer and inner rel.
 *	   (The join rel may already contain paths generated from other
 *	   pairs of rels that add up to the same set of base rels.)
 */
static RelOptInfo *
make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2,
			  JoinType jointype)
{
	RelOptInfo *joinrel;
	List	   *restrictlist;

	/*
	 * Find or build the join RelOptInfo, and compute the restrictlist
	 * that goes with this particular joining.
	 */
	joinrel = get_join_rel(root, rel1, rel2, jointype, &restrictlist);

	/*
	 * Consider paths using each rel as both outer and inner.
	 */
	switch (jointype)
	{
		case JOIN_INNER:
			add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_INNER,
								 restrictlist);
			add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER,
								 restrictlist);
			break;
		case JOIN_LEFT:
			add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT,
								 restrictlist);
			add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_RIGHT,
								 restrictlist);
			break;
		case JOIN_FULL:
			add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_FULL,
								 restrictlist);
			add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_FULL,
								 restrictlist);
			break;
		case JOIN_RIGHT:
			add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_RIGHT,
								 restrictlist);
			add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_LEFT,
								 restrictlist);
			break;
		default:
			elog(ERROR, "make_join_rel: unsupported join type %d",
				 (int) jointype);
			break;
	}

	return joinrel;
}