summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/initsplan.c
blob: 40ab228721de2ea2166be96ffb104db22516f085 (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
/*-------------------------------------------------------------------------
 *
 * initsplan.c
 *	  Target list, qualification, joininfo initialization routines
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.23 1999/02/13 23:16:29 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <sys/types.h>

#include "postgres.h"

#include "nodes/pg_list.h"
#include "nodes/plannodes.h"
#include "nodes/parsenodes.h"
#include "nodes/relation.h"
#include "nodes/makefuncs.h"

#include "access/htup.h"

#include "catalog/pg_type.h"

#include "utils/lsyscache.h"
#include "utils/palloc.h"

#include "optimizer/internal.h"
#include "optimizer/planmain.h"
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"

extern int	Quiet;

static void add_restrict_and_join_to_rel(Query *root, List *clause);
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
					  List *join_relids);
static void add_vars_to_targetlist(Query *root, List *vars, List *join_relids);

static MergeOrder *mergejoinop(Expr *clause);
static Oid	hashjoinop(Expr *clause);


/*****************************************************************************
 *
 *	 TARGET LISTS
 *
 *****************************************************************************/

/*
 * make_var_only_tlist
 *	  Creates rel nodes for every relation mentioned in the target list
 *	  'tlist' (if a node hasn't already been created) and adds them to
 *	  *query_relation_list*.  Creates targetlist entries for each member of
 *	  'tlist' and adds them to the tlist field of the appropriate rel node.
 */
void
make_var_only_tlist(Query *root, List *tlist)
{
	List	   *tlist_vars = NIL;
	List	   *l = NIL;
	List	   *tvar = NIL;

	foreach(l, tlist)
	{
		TargetEntry *entry = (TargetEntry *) lfirst(l);

		tlist_vars = append(tlist_vars, pull_var_clause(entry->expr));
	}

	/* now, the target list only contains Var nodes */
	foreach(tvar, tlist_vars)
	{
		Var		   *var = (Var *) lfirst(tvar);
		Index		varno;
		RelOptInfo *result;

		varno = var->varno;
		result = get_base_rel(root, varno);

		add_var_to_tlist(result, var);
	}
}

/*
 * add_missing_vars_to_tlist
 *	  If we have range variable(s) in the FROM clause that does not appear
 *	  in the target list nor qualifications, we add it to the base relation
 *	  list. For instance, "select f.x from foo f, foo f2" is a join of f and
 *	  f2. Note that if we have "select foo.x from foo f", it also gets turned
 *	  into a join.
 */
void
add_missing_vars_to_tlist(Query *root, List *tlist)
{
	List	   *l;
	int			varno;

	varno = 1;
	foreach(l, root->rtable)
	{
		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
		List	   *relids;
		RelOptInfo *result;
		Var		   *var;

		relids = lconsi(varno, NIL);
		if (rte->inFromCl && !rel_member(relids, root->base_rel_list))
		{
			var = makeVar(varno, ObjectIdAttributeNumber,
						  OIDOID, -1, 0, varno, ObjectIdAttributeNumber);
			/* add it to base_rel_list */
			result = get_base_rel(root, varno);
			add_var_to_tlist(result, var);
		}
		pfree(relids);
		varno++;
	}

	return;
}

/*****************************************************************************
 *
 *	  QUALIFICATIONS
 *
 *****************************************************************************/



/*
 * add_restrict_and_join_to_rels-
 *	  Initializes RestrictInfo and JoinInfo fields of relation entries for all
 *	  relations appearing within clauses.  Creates new relation entries if
 *	  necessary, adding them to *query_relation_list*.
 *
 *	  Returns nothing of interest.
 */
void
add_restrict_and_join_to_rels(Query *root, List *clauses)
{
	List	   *clause;

	foreach(clause, clauses)
		add_restrict_and_join_to_rel(root, lfirst(clause));
	return;
}

/*
 * add_restrict_and_join_to_rel-
 *	  Add clause information to either the 'RestrictInfo' or 'JoinInfo' field
 *	  of a relation entry(depending on whether or not the clause is a join)
 *	  by creating a new RestrictInfo node and setting appropriate fields
 *	  within the nodes.
 *
 *	  Returns nothing of interest.
 */
static void
add_restrict_and_join_to_rel(Query *root, List *clause)
{
	List	   *relids;
	List	   *vars;
	RestrictInfo *restrictinfo = makeNode(RestrictInfo);

	/*
	 * Retrieve all relids and vars contained within the clause.
	 */
	clause_get_relids_vars((Node *) clause, &relids, &vars);

	restrictinfo->clause = (Expr *) clause;
	restrictinfo->notclause = contains_not((Node *) clause);
	restrictinfo->selectivity = 0;
	restrictinfo->indexids = NIL;
	restrictinfo->mergejoinorder = (MergeOrder *) NULL;
	restrictinfo->hashjoinoperator = (Oid) 0;

	if (length(relids) == 1)
	{
		/*
		 * There is only one relation participating in 'clause', so
		 * 'clause' must be a restriction clause.
		 */
		RelOptInfo *rel = get_base_rel(root, lfirsti(relids));

		/*
		 * The selectivity of the clause must be computed regardless of
		 * whether it's a restriction or a join clause
		 */
		if (is_funcclause((Node *) clause))

			/*
			 * XXX If we have a func clause set selectivity to 1/3, really
			 * need a true selectivity function.
			 */
			restrictinfo->selectivity = (Cost) 0.3333333;
		else
			restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);

		rel->restrictinfo = lcons(restrictinfo, rel->restrictinfo);
	}
	else
	{
		/*
		 * 'clause' is a join clause, since there is more than one atom in
		 * the relid list.
		 */
		if (is_funcclause((Node *) clause))
			/*
			 * XXX If we have a func clause set selectivity to 1/3, really
			 * need a true selectivity function.
			 */
			restrictinfo->selectivity = (Cost) 0.3333333;
		else
			restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);

		add_join_info_to_rels(root, restrictinfo, relids);
		/* we are going to be doing a join, so add var to targetlist */
		add_vars_to_targetlist(root, vars, relids);
	}
}

/*
 * add_join_info_to_rels
 *	  For every relation participating in a join clause, add 'restrictinfo' to
 *	  the appropriate joininfo node(creating a new one and adding it to the
 *	  appropriate rel node if necessary).
 *
 * 'restrictinfo' describes the join clause
 * 'join_relids' is the list of relations participating in the join clause
 *
 * Returns nothing.
 *
 */
static void
add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, List *join_relids)
{
	List	   *join_relid;

	foreach(join_relid, join_relids)
	{
		JoinInfo   *joininfo;
		List	   *other_rels = NIL;
		List	   *rel;

		foreach(rel, join_relids)
		{
			if (lfirsti(rel) != lfirsti(join_relid))
				other_rels = lappendi(other_rels, lfirsti(rel));
		}

		joininfo = find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
									  other_rels);
		joininfo->jinfo_restrictinfo = lcons(copyObject((void *) restrictinfo), joininfo->jinfo_restrictinfo);

	}
}

/*
 * add_vars_to_targetlist
 *	  For each variable appearing in a clause,
 *	  (1) If a targetlist entry for the variable is not already present in
 *		  the appropriate relation's target list, add one.
 *	  (2) If a targetlist entry is already present, but the var is part of a
 *		  join clause, add the relids of the join relations to the JoinList
 *		  entry of the targetlist entry.
 *
 *	  'vars' is the list of var nodes
 *	  'join_relids' is the list of relids appearing in the join clause
 *		(if this is a join clause)
 *
 *	  Returns nothing.
 */
static void
add_vars_to_targetlist(Query *root, List *vars, List *join_relids)
{
	Var		   *var;
	List	   *temp = NIL;
	RelOptInfo *rel = (RelOptInfo *) NULL;
	TargetEntry *tlistentry;

	foreach(temp, vars)
	{
		var = (Var *) lfirst(temp);
		rel = get_base_rel(root, var->varno);
		tlistentry = tlistentry_member(var, rel->targetlist);
		if (tlistentry == NULL)
			/* add a new entry */
			add_var_to_tlist(rel, var);
	}
}

/*****************************************************************************
 *
 *	 JOININFO
 *
 *****************************************************************************/

/*
 * init_join_info
 *	  Set the MergeJoinable or HashJoinable field for every joininfo node
 *	  (within a rel node) and the MergeJoinOrder or HashJoinOp field for
 *	  each restrictinfo node(within a joininfo node) for all relations in a
 *	  query.
 *
 *	  Returns nothing.
 */
void
init_join_info(List *rel_list)
{
	List	   *x,
			   *y,
			   *z;
	RelOptInfo *rel;
	JoinInfo   *joininfo;
	RestrictInfo *restrictinfo;
	Expr	   *clause;

	foreach(x, rel_list)
	{
		rel = (RelOptInfo *) lfirst(x);
		foreach(y, rel->joininfo)
		{
			joininfo = (JoinInfo *) lfirst(y);
			foreach(z, joininfo->jinfo_restrictinfo)
			{
				restrictinfo = (RestrictInfo *) lfirst(z);
				clause = restrictinfo->clause;
				if (is_joinable((Node *) clause))
				{
					MergeOrder *sortop = (MergeOrder *) NULL;
					Oid			hashop = (Oid) NULL;

					if (_enable_mergejoin_)
						sortop = mergejoinop(clause);
					if (_enable_hashjoin_)
						hashop = hashjoinop(clause);

					if (sortop)
					{
						restrictinfo->mergejoinorder = sortop;
						joininfo->mergejoinable = true;
					}
					if (hashop)
					{
						restrictinfo->hashjoinoperator = hashop;
						joininfo->hashjoinable = true;
					}
				}
			}
		}
	}
}

/*
 * mergejoinop
 *	  Returns the mergejoin operator of an operator iff 'clause' is
 *	  mergejoinable, i.e., both operands are single vars and the operator is
 *	  a mergejoinable operator.
 */
static MergeOrder *
mergejoinop(Expr *clause)
{
	Oid			leftOp,
				rightOp;
	bool		sortable;

	sortable = op_mergejoinable(((Oper *) clause->oper)->opno,
								(get_leftop(clause))->vartype,
								(get_rightop(clause))->vartype,
								&leftOp,
								&rightOp);

	if (sortable)
	{
		MergeOrder *morder = makeNode(MergeOrder);

		morder->join_operator = ((Oper *) clause->oper)->opno;
		morder->left_operator = leftOp;
		morder->right_operator = rightOp;
		morder->left_type = (get_leftop(clause))->vartype;
		morder->right_type = (get_rightop(clause))->vartype;
		return morder;
	}
	else
		return NULL;
}

/*
 * hashjoinop
 *	  Returns the hashjoin operator of an operator iff 'clause' is
 *	  hashjoinable, i.e., both operands are single vars and the operator is
 *	  a hashjoinable operator.
 */
static Oid
hashjoinop(Expr *clause)
{
	return (op_hashjoinable(((Oper *) clause->oper)->opno,
							(get_leftop(clause))->vartype,
							(get_rightop(clause))->vartype));
}