~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/******************************************************
2
Simple SQL optimizer
3
4
(c) 1997 Innobase Oy
5
6
Created 12/21/1997 Heikki Tuuri
7
*******************************************************/
8
9
#include "pars0opt.h"
10
11
#ifdef UNIV_NONINL
12
#include "pars0opt.ic"
13
#endif
14
15
#include "row0sel.h"
16
#include "row0ins.h"
17
#include "row0upd.h"
18
#include "dict0dict.h"
19
#include "dict0mem.h"
20
#include "que0que.h"
21
#include "pars0grm.h"
22
#include "pars0pars.h"
23
#include "lock0lock.h"
24
25
#define OPT_EQUAL	1	/* comparison by = */
26
#define OPT_COMPARISON	2	/* comparison by <, >, <=, or >= */
27
28
#define OPT_NOT_COND	1
29
#define OPT_END_COND	2
30
#define OPT_TEST_COND	3
31
#define OPT_SCROLL_COND	4
32
33
34
/***********************************************************************
35
Inverts a comparison operator. */
36
static
37
int
38
opt_invert_cmp_op(
39
/*==============*/
40
			/* out: the equivalent operator when the order of
41
			the arguments is switched */
42
	int	op)	/* in: operator */
43
{
44
	if (op == '<') {
45
		return('>');
46
	} else if (op == '>') {
47
		return('<');
48
	} else if (op == '=') {
49
		return('=');
50
	} else if (op == PARS_LE_TOKEN) {
51
		return(PARS_GE_TOKEN);
52
	} else if (op == PARS_GE_TOKEN) {
53
		return(PARS_LE_TOKEN);
54
	} else {
55
		ut_error;
56
	}
57
58
	return(0);
59
}
60
61
/***********************************************************************
62
Checks if the value of an expression can be calculated BEFORE the nth table
63
in a join is accessed. If this is the case, it can possibly be used in an
64
index search for the nth table. */
65
static
66
ibool
67
opt_check_exp_determined_before(
68
/*============================*/
69
					/* out: TRUE if already determined */
70
	que_node_t*	exp,		/* in: expression */
71
	sel_node_t*	sel_node,	/* in: select node */
72
	ulint		nth_table)	/* in: nth table will be accessed */
73
{
74
	func_node_t*	func_node;
75
	sym_node_t*	sym_node;
76
	dict_table_t*	table;
77
	que_node_t*	arg;
78
	ulint		i;
79
80
	ut_ad(exp && sel_node);
81
82
	if (que_node_get_type(exp) == QUE_NODE_FUNC) {
83
		func_node = exp;
84
85
		arg = func_node->args;
86
87
		while (arg) {
88
			if (!opt_check_exp_determined_before(arg, sel_node,
89
							     nth_table)) {
90
				return(FALSE);
91
			}
92
93
			arg = que_node_get_next(arg);
94
		}
95
96
		return(TRUE);
97
	}
98
99
	ut_a(que_node_get_type(exp) == QUE_NODE_SYMBOL);
100
101
	sym_node = exp;
102
103
	if (sym_node->token_type != SYM_COLUMN) {
104
105
		return(TRUE);
106
	}
107
108
	for (i = 0; i < nth_table; i++) {
109
110
		table = sel_node_get_nth_plan(sel_node, i)->table;
111
112
		if (sym_node->table == table) {
113
114
			return(TRUE);
115
		}
116
	}
117
118
	return(FALSE);
119
}
120
121
/***********************************************************************
122
Looks in a comparison condition if a column value is already restricted by
123
it BEFORE the nth table is accessed. */
124
static
125
que_node_t*
126
opt_look_for_col_in_comparison_before(
127
/*==================================*/
128
					/* out: expression restricting the
129
					value of the column, or NULL if not
130
					known */
131
	ulint		cmp_type,	/* in: OPT_EQUAL, OPT_COMPARISON */
132
	ulint		col_no,		/* in: column number */
133
	func_node_t*	search_cond,	/* in: comparison condition */
134
	sel_node_t*	sel_node,	/* in: select node */
135
	ulint		nth_table,	/* in: nth table in a join (a query
136
					from a single table is considered a
137
					join of 1 table) */
138
	ulint*		op)		/* out: comparison operator ('=',
139
					PARS_GE_TOKEN, ... ); this is inverted
140
					if the column appears on the right
141
					side */
142
{
143
	sym_node_t*	sym_node;
144
	dict_table_t*	table;
145
	que_node_t*	exp;
146
	que_node_t*	arg;
147
148
	ut_ad(search_cond);
149
150
	ut_a((search_cond->func == '<')
151
	     || (search_cond->func == '>')
152
	     || (search_cond->func == '=')
153
	     || (search_cond->func == PARS_GE_TOKEN)
154
	     || (search_cond->func == PARS_LE_TOKEN));
155
156
	table = sel_node_get_nth_plan(sel_node, nth_table)->table;
157
158
	if ((cmp_type == OPT_EQUAL) && (search_cond->func != '=')) {
159
160
		return(NULL);
161
162
	} else if ((cmp_type == OPT_COMPARISON)
163
		   && (search_cond->func != '<')
164
		   && (search_cond->func != '>')
165
		   && (search_cond->func != PARS_GE_TOKEN)
166
		   && (search_cond->func != PARS_LE_TOKEN)) {
167
168
		return(NULL);
169
	}
170
171
	arg = search_cond->args;
172
173
	if (que_node_get_type(arg) == QUE_NODE_SYMBOL) {
174
		sym_node = arg;
175
176
		if ((sym_node->token_type == SYM_COLUMN)
177
		    && (sym_node->table == table)
178
		    && (sym_node->col_no == col_no)) {
179
180
			/* sym_node contains the desired column id */
181
182
			/* Check if the expression on the right side of the
183
			operator is already determined */
184
185
			exp = que_node_get_next(arg);
186
187
			if (opt_check_exp_determined_before(exp, sel_node,
188
							    nth_table)) {
189
				*op = search_cond->func;
190
191
				return(exp);
192
			}
193
		}
194
	}
195
196
	exp = search_cond->args;
197
	arg = que_node_get_next(arg);
198
199
	if (que_node_get_type(arg) == QUE_NODE_SYMBOL) {
200
		sym_node = arg;
201
202
		if ((sym_node->token_type == SYM_COLUMN)
203
		    && (sym_node->table == table)
204
		    && (sym_node->col_no == col_no)) {
205
206
			if (opt_check_exp_determined_before(exp, sel_node,
207
							    nth_table)) {
208
				*op = opt_invert_cmp_op(search_cond->func);
209
210
				return(exp);
211
			}
212
		}
213
	}
214
215
	return(NULL);
216
}
217
218
/***********************************************************************
219
Looks in a search condition if a column value is already restricted by the
220
search condition BEFORE the nth table is accessed. Takes into account that
221
if we will fetch in an ascending order, we cannot utilize an upper limit for
222
a column value; in a descending order, respectively, a lower limit. */
223
static
224
que_node_t*
225
opt_look_for_col_in_cond_before(
226
/*============================*/
227
					/* out: expression restricting the
228
					value of the column, or NULL if not
229
					known */
230
	ulint		cmp_type,	/* in: OPT_EQUAL, OPT_COMPARISON */
231
	ulint		col_no,		/* in: column number */
232
	func_node_t*	search_cond,	/* in: search condition or NULL */
233
	sel_node_t*	sel_node,	/* in: select node */
234
	ulint		nth_table,	/* in: nth table in a join (a query
235
					from a single table is considered a
236
					join of 1 table) */
237
	ulint*		op)		/* out: comparison operator ('=',
238
					PARS_GE_TOKEN, ... ) */
239
{
240
	func_node_t*	new_cond;
241
	que_node_t*	exp;
242
243
	if (search_cond == NULL) {
244
245
		return(NULL);
246
	}
247
248
	ut_a(que_node_get_type(search_cond) == QUE_NODE_FUNC);
249
	ut_a(search_cond->func != PARS_OR_TOKEN);
250
	ut_a(search_cond->func != PARS_NOT_TOKEN);
251
252
	if (search_cond->func == PARS_AND_TOKEN) {
253
		new_cond = search_cond->args;
254
255
		exp = opt_look_for_col_in_cond_before(cmp_type, col_no,
256
						      new_cond, sel_node,
257
						      nth_table, op);
258
		if (exp) {
259
260
			return(exp);
261
		}
262
263
		new_cond = que_node_get_next(new_cond);
264
265
		exp = opt_look_for_col_in_cond_before(cmp_type, col_no,
266
						      new_cond, sel_node,
267
						      nth_table, op);
268
		return(exp);
269
	}
270
271
	exp = opt_look_for_col_in_comparison_before(cmp_type, col_no,
272
						    search_cond, sel_node,
273
						    nth_table, op);
274
	if (exp == NULL) {
275
276
		return(NULL);
277
	}
278
279
	/* If we will fetch in an ascending order, we cannot utilize an upper
280
	limit for a column value; in a descending order, respectively, a lower
281
	limit */
282
283
	if (sel_node->asc && ((*op == '<') || (*op == PARS_LE_TOKEN))) {
284
285
		return(NULL);
286
287
	} else if (!sel_node->asc
288
		   && ((*op == '>') || (*op == PARS_GE_TOKEN))) {
289
290
		return(NULL);
291
	}
292
293
	return(exp);
294
}
295
296
/***********************************************************************
297
Calculates the goodness for an index according to a select node. The
298
goodness is 4 times the number of first fields in index whose values we
299
already know exactly in the query. If we have a comparison condition for
300
an additional field, 2 point are added. If the index is unique, and we know
301
all the unique fields for the index we add 1024 points. For a clustered index
302
we add 1 point. */
303
static
304
ulint
305
opt_calc_index_goodness(
306
/*====================*/
307
					/* out: goodness */
308
	dict_index_t*	index,		/* in: index */
309
	sel_node_t*	sel_node,	/* in: parsed select node */
310
	ulint		nth_table,	/* in: nth table in a join */
311
	que_node_t**	index_plan,	/* in/out: comparison expressions for
312
					this index */
313
	ulint*		last_op)	/* out: last comparison operator, if
314
					goodness > 1 */
315
{
316
	que_node_t*	exp;
317
	ulint		goodness;
318
	ulint		n_fields;
319
	ulint		col_no;
320
	ulint		op;
321
	ulint		j;
322
323
	goodness = 0;
324
325
	/* Note that as higher level node pointers in the B-tree contain
326
	page addresses as the last field, we must not put more fields in
327
	the search tuple than dict_index_get_n_unique_in_tree(index); see
328
	the note in btr_cur_search_to_nth_level. */
329
330
	n_fields = dict_index_get_n_unique_in_tree(index);
331
332
	for (j = 0; j < n_fields; j++) {
333
334
		col_no = dict_index_get_nth_col_no(index, j);
335
336
		exp = opt_look_for_col_in_cond_before(
337
			OPT_EQUAL, col_no, sel_node->search_cond,
338
			sel_node, nth_table, &op);
339
		if (exp) {
340
			/* The value for this column is exactly known already
341
			at this stage of the join */
342
343
			index_plan[j] = exp;
344
			*last_op = op;
345
			goodness += 4;
346
		} else {
347
			/* Look for non-equality comparisons */
348
349
			exp = opt_look_for_col_in_cond_before(
350
				OPT_COMPARISON, col_no, sel_node->search_cond,
351
				sel_node, nth_table, &op);
352
			if (exp) {
353
				index_plan[j] = exp;
354
				*last_op = op;
355
				goodness += 2;
356
			}
357
358
			break;
359
		}
360
	}
361
362
	if (goodness >= 4 * dict_index_get_n_unique(index)) {
363
		goodness += 1024;
364
365
		if (index->type & DICT_CLUSTERED) {
366
367
			goodness += 1024;
368
		}
369
	}
370
371
	/* We have to test for goodness here, as last_op may note be set */
372
	if (goodness && index->type & DICT_CLUSTERED) {
373
374
		goodness++;
375
	}
376
377
	return(goodness);
378
}
379
380
/***********************************************************************
381
Calculates the number of matched fields based on an index goodness. */
382
UNIV_INLINE
383
ulint
384
opt_calc_n_fields_from_goodness(
385
/*============================*/
386
				/* out: number of excatly or partially matched
387
				fields */
388
	ulint	goodness)	/* in: goodness */
389
{
390
	return(((goodness % 1024) + 2) / 4);
391
}
392
393
/***********************************************************************
394
Converts a comparison operator to the corresponding search mode PAGE_CUR_GE,
395
... */
396
UNIV_INLINE
397
ulint
398
opt_op_to_search_mode(
399
/*==================*/
400
			/* out: search mode */
401
	ibool	asc,	/* in: TRUE if the rows should be fetched in an
402
			ascending order */
403
	ulint	op)	/* in: operator '=', PARS_GE_TOKEN, ... */
404
{
405
	if (op == '=') {
406
		if (asc) {
407
			return(PAGE_CUR_GE);
408
		} else {
409
			return(PAGE_CUR_LE);
410
		}
411
	} else if (op == '<') {
412
		ut_a(!asc);
413
		return(PAGE_CUR_L);
414
	} else if (op == '>') {
415
		ut_a(asc);
416
		return(PAGE_CUR_G);
417
	} else if (op == PARS_GE_TOKEN) {
418
		ut_a(asc);
419
		return(PAGE_CUR_GE);
420
	} else if (op == PARS_LE_TOKEN) {
421
		ut_a(!asc);
422
		return(PAGE_CUR_LE);
423
	} else {
424
		ut_error;
425
	}
426
427
	return(0);
428
}
429
430
/***********************************************************************
431
Determines if a node is an argument node of a function node. */
432
static
433
ibool
434
opt_is_arg(
435
/*=======*/
436
					/* out: TRUE if is an argument */
437
	que_node_t*	arg_node,	/* in: possible argument node */
438
	func_node_t*	func_node)	/* in: function node */
439
{
440
	que_node_t*	arg;
441
442
	arg = func_node->args;
443
444
	while (arg) {
445
		if (arg == arg_node) {
446
447
			return(TRUE);
448
		}
449
450
		arg = que_node_get_next(arg);
451
	}
452
453
	return(FALSE);
454
}
455
456
/***********************************************************************
457
Decides if the fetching of rows should be made in a descending order, and
458
also checks that the chosen query plan produces a result which satisfies
459
the order-by. */
460
static
461
void
462
opt_check_order_by(
463
/*===============*/
464
	sel_node_t*	sel_node)	/* in: select node; asserts an error
465
					if the plan does not agree with the
466
					order-by */
467
{
468
	order_node_t*	order_node;
469
	dict_table_t*	order_table;
470
	ulint		order_col_no;
471
	plan_t*		plan;
472
	ulint		i;
473
474
	if (!sel_node->order_by) {
475
476
		return;
477
	}
478
479
	order_node = sel_node->order_by;
480
	order_col_no = order_node->column->col_no;
481
	order_table = order_node->column->table;
482
483
	/* If there is an order-by clause, the first non-exactly matched field
484
	in the index used for the last table in the table list should be the
485
	column defined in the order-by clause, and for all the other tables
486
	we should get only at most a single row, otherwise we cannot presently
487
	calculate the order-by, as we have no sort utility */
488
489
	for (i = 0; i < sel_node->n_tables; i++) {
490
491
		plan = sel_node_get_nth_plan(sel_node, i);
492
493
		if (i < sel_node->n_tables - 1) {
494
			ut_a(dict_index_get_n_unique(plan->index)
495
			     <= plan->n_exact_match);
496
		} else {
497
			ut_a(plan->table == order_table);
498
499
			ut_a((dict_index_get_n_unique(plan->index)
500
			      <= plan->n_exact_match)
501
			     || (dict_index_get_nth_col_no(plan->index,
502
							   plan->n_exact_match)
503
				 == order_col_no));
504
		}
505
	}
506
}
507
508
/***********************************************************************
509
Optimizes a select. Decides which indexes to tables to use. The tables
510
are accessed in the order that they were written to the FROM part in the
511
select statement. */
512
static
513
void
514
opt_search_plan_for_table(
515
/*======================*/
516
	sel_node_t*	sel_node,	/* in: parsed select node */
517
	ulint		i,		/* in: this is the ith table */
518
	dict_table_t*	table)		/* in: table */
519
{
520
	plan_t*		plan;
521
	dict_index_t*	index;
522
	dict_index_t*	best_index;
523
	ulint		n_fields;
524
	ulint		goodness;
525
	ulint		last_op		= 75946965;	/* Eliminate a Purify
526
							warning */
527
	ulint		best_goodness;
528
	ulint		best_last_op = 0; /* remove warning */
529
	que_node_t*	index_plan[256];
530
	que_node_t*	best_index_plan[256];
531
532
	plan = sel_node_get_nth_plan(sel_node, i);
533
534
	plan->table = table;
535
	plan->asc = sel_node->asc;
536
	plan->pcur_is_open = FALSE;
537
	plan->cursor_at_end = FALSE;
538
539
	/* Calculate goodness for each index of the table */
540
541
	index = dict_table_get_first_index(table);
542
	best_index = index; /* Eliminate compiler warning */
543
	best_goodness = 0;
544
545
	/* should be do ... until ? comment by Jani */
546
	while (index) {
547
		goodness = opt_calc_index_goodness(index, sel_node, i,
548
						   index_plan, &last_op);
549
		if (goodness > best_goodness) {
550
551
			best_index = index;
552
			best_goodness = goodness;
553
			n_fields = opt_calc_n_fields_from_goodness(goodness);
554
555
			ut_memcpy(best_index_plan, index_plan,
556
				  n_fields * sizeof(void*));
557
			best_last_op = last_op;
558
		}
559
560
		index = dict_table_get_next_index(index);
561
	}
562
563
	plan->index = best_index;
564
565
	n_fields = opt_calc_n_fields_from_goodness(best_goodness);
566
567
	if (n_fields == 0) {
568
		plan->tuple = NULL;
569
		plan->n_exact_match = 0;
570
	} else {
571
		plan->tuple = dtuple_create(pars_sym_tab_global->heap,
572
					    n_fields);
573
		dict_index_copy_types(plan->tuple, plan->index, n_fields);
574
575
		plan->tuple_exps = mem_heap_alloc(pars_sym_tab_global->heap,
576
						  n_fields * sizeof(void*));
577
578
		ut_memcpy(plan->tuple_exps, best_index_plan,
579
			  n_fields * sizeof(void*));
580
		if (best_last_op == '=') {
581
			plan->n_exact_match = n_fields;
582
		} else {
583
			plan->n_exact_match = n_fields - 1;
584
		}
585
586
		plan->mode = opt_op_to_search_mode(sel_node->asc,
587
						   best_last_op);
588
	}
589
590
	if ((best_index->type & DICT_CLUSTERED)
591
	    && (plan->n_exact_match >= dict_index_get_n_unique(best_index))) {
592
593
		plan->unique_search = TRUE;
594
	} else {
595
		plan->unique_search = FALSE;
596
	}
597
598
	plan->old_vers_heap = NULL;
599
600
	btr_pcur_init(&(plan->pcur));
601
	btr_pcur_init(&(plan->clust_pcur));
602
}
603
604
/***********************************************************************
605
Looks at a comparison condition and decides if it can, and need, be tested for
606
a table AFTER the table has been accessed. */
607
static
608
ulint
609
opt_classify_comparison(
610
/*====================*/
611
					/* out: OPT_NOT_COND if not for this
612
					table, else OPT_END_COND,
613
					OPT_TEST_COND, or OPT_SCROLL_COND,
614
					where the last means that the
615
					condition need not be tested, except
616
					when scroll cursors are used */
617
	sel_node_t*	sel_node,	/* in: select node */
618
	ulint		i,		/* in: ith table in the join */
619
	func_node_t*	cond)		/* in: comparison condition */
620
{
621
	plan_t*	plan;
622
	ulint	n_fields;
623
	ulint	op;
624
	ulint	j;
625
626
	ut_ad(cond && sel_node);
627
628
	plan = sel_node_get_nth_plan(sel_node, i);
629
630
	/* Check if the condition is determined after the ith table has been
631
	accessed, but not after the i - 1:th */
632
633
	if (!opt_check_exp_determined_before(cond, sel_node, i + 1)) {
634
635
		return(OPT_NOT_COND);
636
	}
637
638
	if ((i > 0) && opt_check_exp_determined_before(cond, sel_node, i)) {
639
640
		return(OPT_NOT_COND);
641
	}
642
643
	/* If the condition is an exact match condition used in constructing
644
	the search tuple, it is classified as OPT_END_COND */
645
646
	if (plan->tuple) {
647
		n_fields = dtuple_get_n_fields(plan->tuple);
648
	} else {
649
		n_fields = 0;
650
	}
651
652
	for (j = 0; j < plan->n_exact_match; j++) {
653
654
		if (opt_is_arg(plan->tuple_exps[j], cond)) {
655
656
			return(OPT_END_COND);
657
		}
658
	}
659
660
	/* If the condition is an non-exact match condition used in
661
	constructing the search tuple, it is classified as OPT_SCROLL_COND.
662
	When the cursor is positioned, and if a non-scroll cursor is used,
663
	there is no need to test this condition; if a scroll cursor is used
664
	the testing is necessary when the cursor is reversed. */
665
666
	if ((n_fields > plan->n_exact_match)
667
	    && opt_is_arg(plan->tuple_exps[n_fields - 1], cond)) {
668
669
		return(OPT_SCROLL_COND);
670
	}
671
672
	/* If the condition is a non-exact match condition on the first field
673
	in index for which there is no exact match, and it limits the search
674
	range from the opposite side of the search tuple already BEFORE we
675
	access the table, it is classified as OPT_END_COND */
676
677
	if ((dict_index_get_n_fields(plan->index) > plan->n_exact_match)
678
	    && opt_look_for_col_in_comparison_before(
679
		    OPT_COMPARISON,
680
		    dict_index_get_nth_col_no(plan->index,
681
					      plan->n_exact_match),
682
		    cond, sel_node, i, &op)) {
683
684
		if (sel_node->asc && ((op == '<') || (op == PARS_LE_TOKEN))) {
685
686
			return(OPT_END_COND);
687
		}
688
689
		if (!sel_node->asc && ((op == '>') || (op == PARS_GE_TOKEN))) {
690
691
			return(OPT_END_COND);
692
		}
693
	}
694
695
	/* Otherwise, cond is classified as OPT_TEST_COND */
696
697
	return(OPT_TEST_COND);
698
}
699
700
/***********************************************************************
701
Recursively looks for test conditions for a table in a join. */
702
static
703
void
704
opt_find_test_conds(
705
/*================*/
706
	sel_node_t*	sel_node,	/* in: select node */
707
	ulint		i,		/* in: ith table in the join */
708
	func_node_t*	cond)		/* in: conjunction of search
709
					conditions or NULL */
710
{
711
	func_node_t*	new_cond;
712
	ulint		class;
713
	plan_t*		plan;
714
715
	if (cond == NULL) {
716
717
		return;
718
	}
719
720
	if (cond->func == PARS_AND_TOKEN) {
721
		new_cond = cond->args;
722
723
		opt_find_test_conds(sel_node, i, new_cond);
724
725
		new_cond = que_node_get_next(new_cond);
726
727
		opt_find_test_conds(sel_node, i, new_cond);
728
729
		return;
730
	}
731
732
	plan = sel_node_get_nth_plan(sel_node, i);
733
734
	class = opt_classify_comparison(sel_node, i, cond);
735
736
	if (class == OPT_END_COND) {
737
		UT_LIST_ADD_LAST(cond_list, plan->end_conds, cond);
738
739
	} else if (class == OPT_TEST_COND) {
740
		UT_LIST_ADD_LAST(cond_list, plan->other_conds, cond);
741
742
	}
743
}
744
745
/***********************************************************************
746
Normalizes a list of comparison conditions so that a column of the table
747
appears on the left side of the comparison if possible. This is accomplished
748
by switching the arguments of the operator. */
749
static
750
void
751
opt_normalize_cmp_conds(
752
/*====================*/
753
	func_node_t*	cond,	/* in: first in a list of comparison
754
				conditions, or NULL */
755
	dict_table_t*	table)	/* in: table */
756
{
757
	que_node_t*	arg1;
758
	que_node_t*	arg2;
759
	sym_node_t*	sym_node;
760
761
	while (cond) {
762
		arg1 = cond->args;
763
		arg2 = que_node_get_next(arg1);
764
765
		if (que_node_get_type(arg2) == QUE_NODE_SYMBOL) {
766
767
			sym_node = arg2;
768
769
			if ((sym_node->token_type == SYM_COLUMN)
770
			    && (sym_node->table == table)) {
771
772
				/* Switch the order of the arguments */
773
774
				cond->args = arg2;
775
				que_node_list_add_last(NULL, arg2);
776
				que_node_list_add_last(arg2, arg1);
777
778
				/* Invert the operator */
779
				cond->func = opt_invert_cmp_op(cond->func);
780
			}
781
		}
782
783
		cond = UT_LIST_GET_NEXT(cond_list, cond);
784
	}
785
}
786
787
/***********************************************************************
788
Finds out the search condition conjuncts we can, and need, to test as the ith
789
table in a join is accessed. The search tuple can eliminate the need to test
790
some conjuncts. */
791
static
792
void
793
opt_determine_and_normalize_test_conds(
794
/*===================================*/
795
	sel_node_t*	sel_node,	/* in: select node */
796
	ulint		i)		/* in: ith table in the join */
797
{
798
	plan_t*	plan;
799
800
	plan = sel_node_get_nth_plan(sel_node, i);
801
802
	UT_LIST_INIT(plan->end_conds);
803
	UT_LIST_INIT(plan->other_conds);
804
805
	/* Recursively go through the conjuncts and classify them */
806
807
	opt_find_test_conds(sel_node, i, sel_node->search_cond);
808
809
	opt_normalize_cmp_conds(UT_LIST_GET_FIRST(plan->end_conds),
810
				plan->table);
811
812
	ut_a(UT_LIST_GET_LEN(plan->end_conds) >= plan->n_exact_match);
813
}
814
815
/***********************************************************************
816
Looks for occurrences of the columns of the table in the query subgraph and
817
adds them to the list of columns if an occurrence of the same column does not
818
already exist in the list. If the column is already in the list, puts a value
819
indirection to point to the occurrence in the column list, except if the
820
column occurrence we are looking at is in the column list, in which case
821
nothing is done. */
822
823
void
824
opt_find_all_cols(
825
/*==============*/
826
	ibool		copy_val,	/* in: if TRUE, new found columns are
827
					added as columns to copy */
828
	dict_index_t*	index,		/* in: index of the table to use */
829
	sym_node_list_t* col_list,	/* in: base node of a list where
830
					to add new found columns */
831
	plan_t*		plan,		/* in: plan or NULL */
832
	que_node_t*	exp)		/* in: expression or condition or
833
					NULL */
834
{
835
	func_node_t*	func_node;
836
	que_node_t*	arg;
837
	sym_node_t*	sym_node;
838
	sym_node_t*	col_node;
839
	ulint		col_pos;
840
841
	if (exp == NULL) {
842
843
		return;
844
	}
845
846
	if (que_node_get_type(exp) == QUE_NODE_FUNC) {
847
		func_node = exp;
848
849
		arg = func_node->args;
850
851
		while (arg) {
852
			opt_find_all_cols(copy_val, index, col_list, plan,
853
					  arg);
854
			arg = que_node_get_next(arg);
855
		}
856
857
		return;
858
	}
859
860
	ut_a(que_node_get_type(exp) == QUE_NODE_SYMBOL);
861
862
	sym_node = exp;
863
864
	if (sym_node->token_type != SYM_COLUMN) {
865
866
		return;
867
	}
868
869
	if (sym_node->table != index->table) {
870
871
		return;
872
	}
873
874
	/* Look for an occurrence of the same column in the plan column
875
	list */
876
877
	col_node = UT_LIST_GET_FIRST(*col_list);
878
879
	while (col_node) {
880
		if (col_node->col_no == sym_node->col_no) {
881
882
			if (col_node == sym_node) {
883
				/* sym_node was already in a list: do
884
				nothing */
885
886
				return;
887
			}
888
889
			/* Put an indirection */
890
			sym_node->indirection = col_node;
891
			sym_node->alias = col_node;
892
893
			return;
894
		}
895
896
		col_node = UT_LIST_GET_NEXT(col_var_list, col_node);
897
	}
898
899
	/* The same column did not occur in the list: add it */
900
901
	UT_LIST_ADD_LAST(col_var_list, *col_list, sym_node);
902
903
	sym_node->copy_val = copy_val;
904
905
	/* Fill in the field_no fields in sym_node */
906
907
	sym_node->field_nos[SYM_CLUST_FIELD_NO] = dict_index_get_nth_col_pos(
908
		dict_table_get_first_index(index->table), sym_node->col_no);
909
	if (!(index->type & DICT_CLUSTERED)) {
910
911
		ut_a(plan);
912
913
		col_pos = dict_index_get_nth_col_pos(index, sym_node->col_no);
914
915
		if (col_pos == ULINT_UNDEFINED) {
916
917
			plan->must_get_clust = TRUE;
918
		}
919
920
		sym_node->field_nos[SYM_SEC_FIELD_NO] = col_pos;
921
	}
922
}
923
924
/***********************************************************************
925
Looks for occurrences of the columns of the table in conditions which are
926
not yet determined AFTER the join operation has fetched a row in the ith
927
table. The values for these column must be copied to dynamic memory for
928
later use. */
929
static
930
void
931
opt_find_copy_cols(
932
/*===============*/
933
	sel_node_t*	sel_node,	/* in: select node */
934
	ulint		i,		/* in: ith table in the join */
935
	func_node_t*	search_cond)	/* in: search condition or NULL */
936
{
937
	func_node_t*	new_cond;
938
	plan_t*		plan;
939
940
	if (search_cond == NULL) {
941
942
		return;
943
	}
944
945
	ut_ad(que_node_get_type(search_cond) == QUE_NODE_FUNC);
946
947
	if (search_cond->func == PARS_AND_TOKEN) {
948
		new_cond = search_cond->args;
949
950
		opt_find_copy_cols(sel_node, i, new_cond);
951
952
		new_cond = que_node_get_next(new_cond);
953
954
		opt_find_copy_cols(sel_node, i, new_cond);
955
956
		return;
957
	}
958
959
	if (!opt_check_exp_determined_before(search_cond, sel_node, i + 1)) {
960
961
		/* Any ith table columns occurring in search_cond should be
962
		copied, as this condition cannot be tested already on the
963
		fetch from the ith table */
964
965
		plan = sel_node_get_nth_plan(sel_node, i);
966
967
		opt_find_all_cols(TRUE, plan->index, &(plan->columns), plan,
968
				  search_cond);
969
	}
970
}
971
972
/***********************************************************************
973
Classifies the table columns according to whether we use the column only while
974
holding the latch on the page, or whether we have to copy the column value to
975
dynamic memory. Puts the first occurrence of a column to either list in the
976
plan node, and puts indirections to later occurrences of the column. */
977
static
978
void
979
opt_classify_cols(
980
/*==============*/
981
	sel_node_t*	sel_node,	/* in: select node */
982
	ulint		i)		/* in: ith table in the join */
983
{
984
	plan_t*		plan;
985
	que_node_t*	exp;
986
987
	plan = sel_node_get_nth_plan(sel_node, i);
988
989
	/* The final value of the following field will depend on the
990
	environment of the select statement: */
991
992
	plan->must_get_clust = FALSE;
993
994
	UT_LIST_INIT(plan->columns);
995
996
	/* All select list columns should be copied: therefore TRUE as the
997
	first argument */
998
999
	exp = sel_node->select_list;
1000
1001
	while (exp) {
1002
		opt_find_all_cols(TRUE, plan->index, &(plan->columns), plan,
1003
				  exp);
1004
		exp = que_node_get_next(exp);
1005
	}
1006
1007
	opt_find_copy_cols(sel_node, i, sel_node->search_cond);
1008
1009
	/* All remaining columns in the search condition are temporary
1010
	columns: therefore FALSE */
1011
1012
	opt_find_all_cols(FALSE, plan->index, &(plan->columns), plan,
1013
			  sel_node->search_cond);
1014
}
1015
1016
/***********************************************************************
1017
Fills in the info in plan which is used in accessing a clustered index
1018
record. The columns must already be classified for the plan node. */
1019
static
1020
void
1021
opt_clust_access(
1022
/*=============*/
1023
	sel_node_t*	sel_node,	/* in: select node */
1024
	ulint		n)		/* in: nth table in select */
1025
{
1026
	plan_t*		plan;
1027
	dict_table_t*	table;
1028
	dict_index_t*	clust_index;
1029
	dict_index_t*	index;
1030
	mem_heap_t*	heap;
1031
	ulint		n_fields;
1032
	ulint		pos;
1033
	ulint		i;
1034
1035
	plan = sel_node_get_nth_plan(sel_node, n);
1036
1037
	index = plan->index;
1038
1039
	/* The final value of the following field depends on the environment
1040
	of the select statement: */
1041
1042
	plan->no_prefetch = FALSE;
1043
1044
	if (index->type & DICT_CLUSTERED) {
1045
		plan->clust_map = NULL;
1046
		plan->clust_ref = NULL;
1047
1048
		return;
1049
	}
1050
1051
	table = index->table;
1052
1053
	clust_index = dict_table_get_first_index(table);
1054
1055
	n_fields = dict_index_get_n_unique(clust_index);
1056
1057
	heap = pars_sym_tab_global->heap;
1058
1059
	plan->clust_ref = dtuple_create(heap, n_fields);
1060
1061
	dict_index_copy_types(plan->clust_ref, clust_index, n_fields);
1062
1063
	plan->clust_map = mem_heap_alloc(heap, n_fields * sizeof(ulint));
1064
1065
	for (i = 0; i < n_fields; i++) {
1066
		pos = dict_index_get_nth_field_pos(index, clust_index, i);
1067
1068
		ut_a(pos != ULINT_UNDEFINED);
1069
1070
		/* We optimize here only queries to InnoDB's internal system
1071
		tables, and they should not contain column prefix indexes. */
1072
1073
		if (dict_index_get_nth_field(index, pos)->prefix_len != 0
1074
		    || dict_index_get_nth_field(clust_index, i)
1075
		    ->prefix_len != 0) {
1076
			fprintf(stderr,
1077
				"InnoDB: Error in pars0opt.c:"
1078
				" table %s has prefix_len != 0\n",
1079
				index->table_name);
1080
		}
1081
1082
		*(plan->clust_map + i) = pos;
1083
1084
		ut_ad(pos != ULINT_UNDEFINED);
1085
	}
1086
}
1087
1088
/***********************************************************************
1089
Optimizes a select. Decides which indexes to tables to use. The tables
1090
are accessed in the order that they were written to the FROM part in the
1091
select statement. */
1092
1093
void
1094
opt_search_plan(
1095
/*============*/
1096
	sel_node_t*	sel_node)	/* in: parsed select node */
1097
{
1098
	sym_node_t*	table_node;
1099
	dict_table_t*	table;
1100
	order_node_t*	order_by;
1101
	ulint		i;
1102
1103
	sel_node->plans = mem_heap_alloc(pars_sym_tab_global->heap,
1104
					 sel_node->n_tables * sizeof(plan_t));
1105
1106
	/* Analyze the search condition to find out what we know at each
1107
	join stage about the conditions that the columns of a table should
1108
	satisfy */
1109
1110
	table_node = sel_node->table_list;
1111
1112
	if (sel_node->order_by == NULL) {
1113
		sel_node->asc = TRUE;
1114
	} else {
1115
		order_by = sel_node->order_by;
1116
1117
		sel_node->asc = order_by->asc;
1118
	}
1119
1120
	for (i = 0; i < sel_node->n_tables; i++) {
1121
1122
		table = table_node->table;
1123
1124
		/* Choose index through which to access the table */
1125
1126
		opt_search_plan_for_table(sel_node, i, table);
1127
1128
		/* Determine the search condition conjuncts we can test at
1129
		this table; normalize the end conditions */
1130
1131
		opt_determine_and_normalize_test_conds(sel_node, i);
1132
1133
		table_node = que_node_get_next(table_node);
1134
	}
1135
1136
	table_node = sel_node->table_list;
1137
1138
	for (i = 0; i < sel_node->n_tables; i++) {
1139
1140
		/* Classify the table columns into those we only need to access
1141
		but not copy, and to those we must copy to dynamic memory */
1142
1143
		opt_classify_cols(sel_node, i);
1144
1145
		/* Calculate possible info for accessing the clustered index
1146
		record */
1147
1148
		opt_clust_access(sel_node, i);
1149
1150
		table_node = que_node_get_next(table_node);
1151
	}
1152
1153
	/* Check that the plan obeys a possible order-by clause: if not,
1154
	an assertion error occurs */
1155
1156
	opt_check_order_by(sel_node);
1157
1158
#ifdef UNIV_SQL_DEBUG
1159
	opt_print_query_plan(sel_node);
1160
#endif
1161
}
1162
1163
/************************************************************************
1164
Prints info of a query plan. */
1165
1166
void
1167
opt_print_query_plan(
1168
/*=================*/
1169
	sel_node_t*	sel_node)	/* in: select node */
1170
{
1171
	plan_t*	plan;
1172
	ulint	n_fields;
1173
	ulint	i;
1174
1175
	fputs("QUERY PLAN FOR A SELECT NODE\n", stderr);
1176
1177
	fputs(sel_node->asc ? "Asc. search; " : "Desc. search; ", stderr);
1178
1179
	if (sel_node->set_x_locks) {
1180
		fputs("sets row x-locks; ", stderr);
1181
		ut_a(sel_node->row_lock_mode == LOCK_X);
1182
		ut_a(!sel_node->consistent_read);
1183
	} else if (sel_node->consistent_read) {
1184
		fputs("consistent read; ", stderr);
1185
	} else {
1186
		ut_a(sel_node->row_lock_mode == LOCK_S);
1187
		fputs("sets row s-locks; ", stderr);
1188
	}
1189
1190
	putc('\n', stderr);
1191
1192
	for (i = 0; i < sel_node->n_tables; i++) {
1193
		plan = sel_node_get_nth_plan(sel_node, i);
1194
1195
		if (plan->tuple) {
1196
			n_fields = dtuple_get_n_fields(plan->tuple);
1197
		} else {
1198
			n_fields = 0;
1199
		}
1200
1201
		fputs("Table ", stderr);
1202
		dict_index_name_print(stderr, NULL, plan->index);
1203
		fprintf(stderr,"; exact m. %lu, match %lu, end conds %lu\n",
1204
			(unsigned long) plan->n_exact_match,
1205
			(unsigned long) n_fields,
1206
			(unsigned long) UT_LIST_GET_LEN(plan->end_conds));
1207
	}
1208
}