mySQL 5.0.11 sources for tomato
[tomato.git] / release / src / router / mysql / storage / innodb_plugin / pars / pars0opt.c
blob12fcb7a110e4576b285b2d81b346f3032d8284c1
1 /*****************************************************************************
3 Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved.
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *****************************************************************************/
19 /**************************************************//**
20 @file pars/pars0opt.c
21 Simple SQL optimizer
23 Created 12/21/1997 Heikki Tuuri
24 *******************************************************/
26 #include "pars0opt.h"
28 #ifdef UNIV_NONINL
29 #include "pars0opt.ic"
30 #endif
32 #include "row0sel.h"
33 #include "row0ins.h"
34 #include "row0upd.h"
35 #include "dict0dict.h"
36 #include "dict0mem.h"
37 #include "que0que.h"
38 #include "pars0grm.h"
39 #include "pars0pars.h"
40 #include "lock0lock.h"
42 #define OPT_EQUAL 1 /* comparison by = */
43 #define OPT_COMPARISON 2 /* comparison by <, >, <=, or >= */
45 #define OPT_NOT_COND 1
46 #define OPT_END_COND 2
47 #define OPT_TEST_COND 3
48 #define OPT_SCROLL_COND 4
51 /*******************************************************************//**
52 Inverts a comparison operator.
53 @return the equivalent operator when the order of the arguments is switched */
54 static
55 int
56 opt_invert_cmp_op(
57 /*==============*/
58 int op) /*!< in: operator */
60 if (op == '<') {
61 return('>');
62 } else if (op == '>') {
63 return('<');
64 } else if (op == '=') {
65 return('=');
66 } else if (op == PARS_LE_TOKEN) {
67 return(PARS_GE_TOKEN);
68 } else if (op == PARS_GE_TOKEN) {
69 return(PARS_LE_TOKEN);
70 } else {
71 ut_error;
74 return(0);
77 /*******************************************************************//**
78 Checks if the value of an expression can be calculated BEFORE the nth table
79 in a join is accessed. If this is the case, it can possibly be used in an
80 index search for the nth table.
81 @return TRUE if already determined */
82 static
83 ibool
84 opt_check_exp_determined_before(
85 /*============================*/
86 que_node_t* exp, /*!< in: expression */
87 sel_node_t* sel_node, /*!< in: select node */
88 ulint nth_table) /*!< in: nth table will be accessed */
90 func_node_t* func_node;
91 sym_node_t* sym_node;
92 dict_table_t* table;
93 que_node_t* arg;
94 ulint i;
96 ut_ad(exp && sel_node);
98 if (que_node_get_type(exp) == QUE_NODE_FUNC) {
99 func_node = exp;
101 arg = func_node->args;
103 while (arg) {
104 if (!opt_check_exp_determined_before(arg, sel_node,
105 nth_table)) {
106 return(FALSE);
109 arg = que_node_get_next(arg);
112 return(TRUE);
115 ut_a(que_node_get_type(exp) == QUE_NODE_SYMBOL);
117 sym_node = exp;
119 if (sym_node->token_type != SYM_COLUMN) {
121 return(TRUE);
124 for (i = 0; i < nth_table; i++) {
126 table = sel_node_get_nth_plan(sel_node, i)->table;
128 if (sym_node->table == table) {
130 return(TRUE);
134 return(FALSE);
137 /*******************************************************************//**
138 Looks in a comparison condition if a column value is already restricted by
139 it BEFORE the nth table is accessed.
140 @return expression restricting the value of the column, or NULL if not known */
141 static
142 que_node_t*
143 opt_look_for_col_in_comparison_before(
144 /*==================================*/
145 ulint cmp_type, /*!< in: OPT_EQUAL, OPT_COMPARISON */
146 ulint col_no, /*!< in: column number */
147 func_node_t* search_cond, /*!< in: comparison condition */
148 sel_node_t* sel_node, /*!< in: select node */
149 ulint nth_table, /*!< in: nth table in a join (a query
150 from a single table is considered a
151 join of 1 table) */
152 ulint* op) /*!< out: comparison operator ('=',
153 PARS_GE_TOKEN, ... ); this is inverted
154 if the column appears on the right
155 side */
157 sym_node_t* sym_node;
158 dict_table_t* table;
159 que_node_t* exp;
160 que_node_t* arg;
162 ut_ad(search_cond);
164 ut_a((search_cond->func == '<')
165 || (search_cond->func == '>')
166 || (search_cond->func == '=')
167 || (search_cond->func == PARS_GE_TOKEN)
168 || (search_cond->func == PARS_LE_TOKEN));
170 table = sel_node_get_nth_plan(sel_node, nth_table)->table;
172 if ((cmp_type == OPT_EQUAL) && (search_cond->func != '=')) {
174 return(NULL);
176 } else if ((cmp_type == OPT_COMPARISON)
177 && (search_cond->func != '<')
178 && (search_cond->func != '>')
179 && (search_cond->func != PARS_GE_TOKEN)
180 && (search_cond->func != PARS_LE_TOKEN)) {
182 return(NULL);
185 arg = search_cond->args;
187 if (que_node_get_type(arg) == QUE_NODE_SYMBOL) {
188 sym_node = arg;
190 if ((sym_node->token_type == SYM_COLUMN)
191 && (sym_node->table == table)
192 && (sym_node->col_no == col_no)) {
194 /* sym_node contains the desired column id */
196 /* Check if the expression on the right side of the
197 operator is already determined */
199 exp = que_node_get_next(arg);
201 if (opt_check_exp_determined_before(exp, sel_node,
202 nth_table)) {
203 *op = search_cond->func;
205 return(exp);
210 exp = search_cond->args;
211 arg = que_node_get_next(arg);
213 if (que_node_get_type(arg) == QUE_NODE_SYMBOL) {
214 sym_node = arg;
216 if ((sym_node->token_type == SYM_COLUMN)
217 && (sym_node->table == table)
218 && (sym_node->col_no == col_no)) {
220 if (opt_check_exp_determined_before(exp, sel_node,
221 nth_table)) {
222 *op = opt_invert_cmp_op(search_cond->func);
224 return(exp);
229 return(NULL);
232 /*******************************************************************//**
233 Looks in a search condition if a column value is already restricted by the
234 search condition BEFORE the nth table is accessed. Takes into account that
235 if we will fetch in an ascending order, we cannot utilize an upper limit for
236 a column value; in a descending order, respectively, a lower limit.
237 @return expression restricting the value of the column, or NULL if not known */
238 static
239 que_node_t*
240 opt_look_for_col_in_cond_before(
241 /*============================*/
242 ulint cmp_type, /*!< in: OPT_EQUAL, OPT_COMPARISON */
243 ulint col_no, /*!< in: column number */
244 func_node_t* search_cond, /*!< in: search condition or NULL */
245 sel_node_t* sel_node, /*!< in: select node */
246 ulint nth_table, /*!< in: nth table in a join (a query
247 from a single table is considered a
248 join of 1 table) */
249 ulint* op) /*!< out: comparison operator ('=',
250 PARS_GE_TOKEN, ... ) */
252 func_node_t* new_cond;
253 que_node_t* exp;
255 if (search_cond == NULL) {
257 return(NULL);
260 ut_a(que_node_get_type(search_cond) == QUE_NODE_FUNC);
261 ut_a(search_cond->func != PARS_OR_TOKEN);
262 ut_a(search_cond->func != PARS_NOT_TOKEN);
264 if (search_cond->func == PARS_AND_TOKEN) {
265 new_cond = search_cond->args;
267 exp = opt_look_for_col_in_cond_before(cmp_type, col_no,
268 new_cond, sel_node,
269 nth_table, op);
270 if (exp) {
272 return(exp);
275 new_cond = que_node_get_next(new_cond);
277 exp = opt_look_for_col_in_cond_before(cmp_type, col_no,
278 new_cond, sel_node,
279 nth_table, op);
280 return(exp);
283 exp = opt_look_for_col_in_comparison_before(cmp_type, col_no,
284 search_cond, sel_node,
285 nth_table, op);
286 if (exp == NULL) {
288 return(NULL);
291 /* If we will fetch in an ascending order, we cannot utilize an upper
292 limit for a column value; in a descending order, respectively, a lower
293 limit */
295 if (sel_node->asc && ((*op == '<') || (*op == PARS_LE_TOKEN))) {
297 return(NULL);
299 } else if (!sel_node->asc
300 && ((*op == '>') || (*op == PARS_GE_TOKEN))) {
302 return(NULL);
305 return(exp);
308 /*******************************************************************//**
309 Calculates the goodness for an index according to a select node. The
310 goodness is 4 times the number of first fields in index whose values we
311 already know exactly in the query. If we have a comparison condition for
312 an additional field, 2 point are added. If the index is unique, and we know
313 all the unique fields for the index we add 1024 points. For a clustered index
314 we add 1 point.
315 @return goodness */
316 static
317 ulint
318 opt_calc_index_goodness(
319 /*====================*/
320 dict_index_t* index, /*!< in: index */
321 sel_node_t* sel_node, /*!< in: parsed select node */
322 ulint nth_table, /*!< in: nth table in a join */
323 que_node_t** index_plan, /*!< in/out: comparison expressions for
324 this index */
325 ulint* last_op) /*!< out: last comparison operator, if
326 goodness > 1 */
328 que_node_t* exp;
329 ulint goodness;
330 ulint n_fields;
331 ulint col_no;
332 ulint op;
333 ulint j;
335 goodness = 0;
337 /* Note that as higher level node pointers in the B-tree contain
338 page addresses as the last field, we must not put more fields in
339 the search tuple than dict_index_get_n_unique_in_tree(index); see
340 the note in btr_cur_search_to_nth_level. */
342 n_fields = dict_index_get_n_unique_in_tree(index);
344 for (j = 0; j < n_fields; j++) {
346 col_no = dict_index_get_nth_col_no(index, j);
348 exp = opt_look_for_col_in_cond_before(
349 OPT_EQUAL, col_no, sel_node->search_cond,
350 sel_node, nth_table, &op);
351 if (exp) {
352 /* The value for this column is exactly known already
353 at this stage of the join */
355 index_plan[j] = exp;
356 *last_op = op;
357 goodness += 4;
358 } else {
359 /* Look for non-equality comparisons */
361 exp = opt_look_for_col_in_cond_before(
362 OPT_COMPARISON, col_no, sel_node->search_cond,
363 sel_node, nth_table, &op);
364 if (exp) {
365 index_plan[j] = exp;
366 *last_op = op;
367 goodness += 2;
370 break;
374 if (goodness >= 4 * dict_index_get_n_unique(index)) {
375 goodness += 1024;
377 if (dict_index_is_clust(index)) {
379 goodness += 1024;
383 /* We have to test for goodness here, as last_op may note be set */
384 if (goodness && dict_index_is_clust(index)) {
386 goodness++;
389 return(goodness);
392 /*******************************************************************//**
393 Calculates the number of matched fields based on an index goodness.
394 @return number of excatly or partially matched fields */
395 UNIV_INLINE
396 ulint
397 opt_calc_n_fields_from_goodness(
398 /*============================*/
399 ulint goodness) /*!< in: goodness */
401 return(((goodness % 1024) + 2) / 4);
404 /*******************************************************************//**
405 Converts a comparison operator to the corresponding search mode PAGE_CUR_GE,
407 @return search mode */
408 UNIV_INLINE
409 ulint
410 opt_op_to_search_mode(
411 /*==================*/
412 ibool asc, /*!< in: TRUE if the rows should be fetched in an
413 ascending order */
414 ulint op) /*!< in: operator '=', PARS_GE_TOKEN, ... */
416 if (op == '=') {
417 if (asc) {
418 return(PAGE_CUR_GE);
419 } else {
420 return(PAGE_CUR_LE);
422 } else if (op == '<') {
423 ut_a(!asc);
424 return(PAGE_CUR_L);
425 } else if (op == '>') {
426 ut_a(asc);
427 return(PAGE_CUR_G);
428 } else if (op == PARS_GE_TOKEN) {
429 ut_a(asc);
430 return(PAGE_CUR_GE);
431 } else if (op == PARS_LE_TOKEN) {
432 ut_a(!asc);
433 return(PAGE_CUR_LE);
434 } else {
435 ut_error;
438 return(0);
441 /*******************************************************************//**
442 Determines if a node is an argument node of a function node.
443 @return TRUE if is an argument */
444 static
445 ibool
446 opt_is_arg(
447 /*=======*/
448 que_node_t* arg_node, /*!< in: possible argument node */
449 func_node_t* func_node) /*!< in: function node */
451 que_node_t* arg;
453 arg = func_node->args;
455 while (arg) {
456 if (arg == arg_node) {
458 return(TRUE);
461 arg = que_node_get_next(arg);
464 return(FALSE);
467 /*******************************************************************//**
468 Decides if the fetching of rows should be made in a descending order, and
469 also checks that the chosen query plan produces a result which satisfies
470 the order-by. */
471 static
472 void
473 opt_check_order_by(
474 /*===============*/
475 sel_node_t* sel_node) /*!< in: select node; asserts an error
476 if the plan does not agree with the
477 order-by */
479 order_node_t* order_node;
480 dict_table_t* order_table;
481 ulint order_col_no;
482 plan_t* plan;
483 ulint i;
485 if (!sel_node->order_by) {
487 return;
490 order_node = sel_node->order_by;
491 order_col_no = order_node->column->col_no;
492 order_table = order_node->column->table;
494 /* If there is an order-by clause, the first non-exactly matched field
495 in the index used for the last table in the table list should be the
496 column defined in the order-by clause, and for all the other tables
497 we should get only at most a single row, otherwise we cannot presently
498 calculate the order-by, as we have no sort utility */
500 for (i = 0; i < sel_node->n_tables; i++) {
502 plan = sel_node_get_nth_plan(sel_node, i);
504 if (i < sel_node->n_tables - 1) {
505 ut_a(dict_index_get_n_unique(plan->index)
506 <= plan->n_exact_match);
507 } else {
508 ut_a(plan->table == order_table);
510 ut_a((dict_index_get_n_unique(plan->index)
511 <= plan->n_exact_match)
512 || (dict_index_get_nth_col_no(plan->index,
513 plan->n_exact_match)
514 == order_col_no));
519 /*******************************************************************//**
520 Optimizes a select. Decides which indexes to tables to use. The tables
521 are accessed in the order that they were written to the FROM part in the
522 select statement. */
523 static
524 void
525 opt_search_plan_for_table(
526 /*======================*/
527 sel_node_t* sel_node, /*!< in: parsed select node */
528 ulint i, /*!< in: this is the ith table */
529 dict_table_t* table) /*!< in: table */
531 plan_t* plan;
532 dict_index_t* index;
533 dict_index_t* best_index;
534 ulint n_fields;
535 ulint goodness;
536 ulint last_op = 75946965; /* Eliminate a Purify
537 warning */
538 ulint best_goodness;
539 ulint best_last_op = 0; /* remove warning */
540 que_node_t* index_plan[256];
541 que_node_t* best_index_plan[256];
543 plan = sel_node_get_nth_plan(sel_node, i);
545 plan->table = table;
546 plan->asc = sel_node->asc;
547 plan->pcur_is_open = FALSE;
548 plan->cursor_at_end = FALSE;
550 /* Calculate goodness for each index of the table */
552 index = dict_table_get_first_index(table);
553 best_index = index; /* Eliminate compiler warning */
554 best_goodness = 0;
556 /* should be do ... until ? comment by Jani */
557 while (index) {
558 goodness = opt_calc_index_goodness(index, sel_node, i,
559 index_plan, &last_op);
560 if (goodness > best_goodness) {
562 best_index = index;
563 best_goodness = goodness;
564 n_fields = opt_calc_n_fields_from_goodness(goodness);
566 ut_memcpy(best_index_plan, index_plan,
567 n_fields * sizeof(void*));
568 best_last_op = last_op;
571 index = dict_table_get_next_index(index);
574 plan->index = best_index;
576 n_fields = opt_calc_n_fields_from_goodness(best_goodness);
578 if (n_fields == 0) {
579 plan->tuple = NULL;
580 plan->n_exact_match = 0;
581 } else {
582 plan->tuple = dtuple_create(pars_sym_tab_global->heap,
583 n_fields);
584 dict_index_copy_types(plan->tuple, plan->index, n_fields);
586 plan->tuple_exps = mem_heap_alloc(pars_sym_tab_global->heap,
587 n_fields * sizeof(void*));
589 ut_memcpy(plan->tuple_exps, best_index_plan,
590 n_fields * sizeof(void*));
591 if (best_last_op == '=') {
592 plan->n_exact_match = n_fields;
593 } else {
594 plan->n_exact_match = n_fields - 1;
597 plan->mode = opt_op_to_search_mode(sel_node->asc,
598 best_last_op);
601 if (dict_index_is_clust(best_index)
602 && (plan->n_exact_match >= dict_index_get_n_unique(best_index))) {
604 plan->unique_search = TRUE;
605 } else {
606 plan->unique_search = FALSE;
609 plan->old_vers_heap = NULL;
611 btr_pcur_init(&(plan->pcur));
612 btr_pcur_init(&(plan->clust_pcur));
615 /*******************************************************************//**
616 Looks at a comparison condition and decides if it can, and need, be tested for
617 a table AFTER the table has been accessed.
618 @return OPT_NOT_COND if not for this table, else OPT_END_COND,
619 OPT_TEST_COND, or OPT_SCROLL_COND, where the last means that the
620 condition need not be tested, except when scroll cursors are used */
621 static
622 ulint
623 opt_classify_comparison(
624 /*====================*/
625 sel_node_t* sel_node, /*!< in: select node */
626 ulint i, /*!< in: ith table in the join */
627 func_node_t* cond) /*!< in: comparison condition */
629 plan_t* plan;
630 ulint n_fields;
631 ulint op;
632 ulint j;
634 ut_ad(cond && sel_node);
636 plan = sel_node_get_nth_plan(sel_node, i);
638 /* Check if the condition is determined after the ith table has been
639 accessed, but not after the i - 1:th */
641 if (!opt_check_exp_determined_before(cond, sel_node, i + 1)) {
643 return(OPT_NOT_COND);
646 if ((i > 0) && opt_check_exp_determined_before(cond, sel_node, i)) {
648 return(OPT_NOT_COND);
651 /* If the condition is an exact match condition used in constructing
652 the search tuple, it is classified as OPT_END_COND */
654 if (plan->tuple) {
655 n_fields = dtuple_get_n_fields(plan->tuple);
656 } else {
657 n_fields = 0;
660 for (j = 0; j < plan->n_exact_match; j++) {
662 if (opt_is_arg(plan->tuple_exps[j], cond)) {
664 return(OPT_END_COND);
668 /* If the condition is an non-exact match condition used in
669 constructing the search tuple, it is classified as OPT_SCROLL_COND.
670 When the cursor is positioned, and if a non-scroll cursor is used,
671 there is no need to test this condition; if a scroll cursor is used
672 the testing is necessary when the cursor is reversed. */
674 if ((n_fields > plan->n_exact_match)
675 && opt_is_arg(plan->tuple_exps[n_fields - 1], cond)) {
677 return(OPT_SCROLL_COND);
680 /* If the condition is a non-exact match condition on the first field
681 in index for which there is no exact match, and it limits the search
682 range from the opposite side of the search tuple already BEFORE we
683 access the table, it is classified as OPT_END_COND */
685 if ((dict_index_get_n_fields(plan->index) > plan->n_exact_match)
686 && opt_look_for_col_in_comparison_before(
687 OPT_COMPARISON,
688 dict_index_get_nth_col_no(plan->index,
689 plan->n_exact_match),
690 cond, sel_node, i, &op)) {
692 if (sel_node->asc && ((op == '<') || (op == PARS_LE_TOKEN))) {
694 return(OPT_END_COND);
697 if (!sel_node->asc && ((op == '>') || (op == PARS_GE_TOKEN))) {
699 return(OPT_END_COND);
703 /* Otherwise, cond is classified as OPT_TEST_COND */
705 return(OPT_TEST_COND);
708 /*******************************************************************//**
709 Recursively looks for test conditions for a table in a join. */
710 static
711 void
712 opt_find_test_conds(
713 /*================*/
714 sel_node_t* sel_node, /*!< in: select node */
715 ulint i, /*!< in: ith table in the join */
716 func_node_t* cond) /*!< in: conjunction of search
717 conditions or NULL */
719 func_node_t* new_cond;
720 ulint class;
721 plan_t* plan;
723 if (cond == NULL) {
725 return;
728 if (cond->func == PARS_AND_TOKEN) {
729 new_cond = cond->args;
731 opt_find_test_conds(sel_node, i, new_cond);
733 new_cond = que_node_get_next(new_cond);
735 opt_find_test_conds(sel_node, i, new_cond);
737 return;
740 plan = sel_node_get_nth_plan(sel_node, i);
742 class = opt_classify_comparison(sel_node, i, cond);
744 if (class == OPT_END_COND) {
745 UT_LIST_ADD_LAST(cond_list, plan->end_conds, cond);
747 } else if (class == OPT_TEST_COND) {
748 UT_LIST_ADD_LAST(cond_list, plan->other_conds, cond);
753 /*******************************************************************//**
754 Normalizes a list of comparison conditions so that a column of the table
755 appears on the left side of the comparison if possible. This is accomplished
756 by switching the arguments of the operator. */
757 static
758 void
759 opt_normalize_cmp_conds(
760 /*====================*/
761 func_node_t* cond, /*!< in: first in a list of comparison
762 conditions, or NULL */
763 dict_table_t* table) /*!< in: table */
765 que_node_t* arg1;
766 que_node_t* arg2;
767 sym_node_t* sym_node;
769 while (cond) {
770 arg1 = cond->args;
771 arg2 = que_node_get_next(arg1);
773 if (que_node_get_type(arg2) == QUE_NODE_SYMBOL) {
775 sym_node = arg2;
777 if ((sym_node->token_type == SYM_COLUMN)
778 && (sym_node->table == table)) {
780 /* Switch the order of the arguments */
782 cond->args = arg2;
783 que_node_list_add_last(NULL, arg2);
784 que_node_list_add_last(arg2, arg1);
786 /* Invert the operator */
787 cond->func = opt_invert_cmp_op(cond->func);
791 cond = UT_LIST_GET_NEXT(cond_list, cond);
795 /*******************************************************************//**
796 Finds out the search condition conjuncts we can, and need, to test as the ith
797 table in a join is accessed. The search tuple can eliminate the need to test
798 some conjuncts. */
799 static
800 void
801 opt_determine_and_normalize_test_conds(
802 /*===================================*/
803 sel_node_t* sel_node, /*!< in: select node */
804 ulint i) /*!< in: ith table in the join */
806 plan_t* plan;
808 plan = sel_node_get_nth_plan(sel_node, i);
810 UT_LIST_INIT(plan->end_conds);
811 UT_LIST_INIT(plan->other_conds);
813 /* Recursively go through the conjuncts and classify them */
815 opt_find_test_conds(sel_node, i, sel_node->search_cond);
817 opt_normalize_cmp_conds(UT_LIST_GET_FIRST(plan->end_conds),
818 plan->table);
820 ut_a(UT_LIST_GET_LEN(plan->end_conds) >= plan->n_exact_match);
823 /*******************************************************************//**
824 Looks for occurrences of the columns of the table in the query subgraph and
825 adds them to the list of columns if an occurrence of the same column does not
826 already exist in the list. If the column is already in the list, puts a value
827 indirection to point to the occurrence in the column list, except if the
828 column occurrence we are looking at is in the column list, in which case
829 nothing is done. */
830 UNIV_INTERN
831 void
832 opt_find_all_cols(
833 /*==============*/
834 ibool copy_val, /*!< in: if TRUE, new found columns are
835 added as columns to copy */
836 dict_index_t* index, /*!< in: index of the table to use */
837 sym_node_list_t* col_list, /*!< in: base node of a list where
838 to add new found columns */
839 plan_t* plan, /*!< in: plan or NULL */
840 que_node_t* exp) /*!< in: expression or condition or
841 NULL */
843 func_node_t* func_node;
844 que_node_t* arg;
845 sym_node_t* sym_node;
846 sym_node_t* col_node;
847 ulint col_pos;
849 if (exp == NULL) {
851 return;
854 if (que_node_get_type(exp) == QUE_NODE_FUNC) {
855 func_node = exp;
857 arg = func_node->args;
859 while (arg) {
860 opt_find_all_cols(copy_val, index, col_list, plan,
861 arg);
862 arg = que_node_get_next(arg);
865 return;
868 ut_a(que_node_get_type(exp) == QUE_NODE_SYMBOL);
870 sym_node = exp;
872 if (sym_node->token_type != SYM_COLUMN) {
874 return;
877 if (sym_node->table != index->table) {
879 return;
882 /* Look for an occurrence of the same column in the plan column
883 list */
885 col_node = UT_LIST_GET_FIRST(*col_list);
887 while (col_node) {
888 if (col_node->col_no == sym_node->col_no) {
890 if (col_node == sym_node) {
891 /* sym_node was already in a list: do
892 nothing */
894 return;
897 /* Put an indirection */
898 sym_node->indirection = col_node;
899 sym_node->alias = col_node;
901 return;
904 col_node = UT_LIST_GET_NEXT(col_var_list, col_node);
907 /* The same column did not occur in the list: add it */
909 UT_LIST_ADD_LAST(col_var_list, *col_list, sym_node);
911 sym_node->copy_val = copy_val;
913 /* Fill in the field_no fields in sym_node */
915 sym_node->field_nos[SYM_CLUST_FIELD_NO] = dict_index_get_nth_col_pos(
916 dict_table_get_first_index(index->table), sym_node->col_no);
917 if (!dict_index_is_clust(index)) {
919 ut_a(plan);
921 col_pos = dict_index_get_nth_col_pos(index, sym_node->col_no);
923 if (col_pos == ULINT_UNDEFINED) {
925 plan->must_get_clust = TRUE;
928 sym_node->field_nos[SYM_SEC_FIELD_NO] = col_pos;
932 /*******************************************************************//**
933 Looks for occurrences of the columns of the table in conditions which are
934 not yet determined AFTER the join operation has fetched a row in the ith
935 table. The values for these column must be copied to dynamic memory for
936 later use. */
937 static
938 void
939 opt_find_copy_cols(
940 /*===============*/
941 sel_node_t* sel_node, /*!< in: select node */
942 ulint i, /*!< in: ith table in the join */
943 func_node_t* search_cond) /*!< in: search condition or NULL */
945 func_node_t* new_cond;
946 plan_t* plan;
948 if (search_cond == NULL) {
950 return;
953 ut_ad(que_node_get_type(search_cond) == QUE_NODE_FUNC);
955 if (search_cond->func == PARS_AND_TOKEN) {
956 new_cond = search_cond->args;
958 opt_find_copy_cols(sel_node, i, new_cond);
960 new_cond = que_node_get_next(new_cond);
962 opt_find_copy_cols(sel_node, i, new_cond);
964 return;
967 if (!opt_check_exp_determined_before(search_cond, sel_node, i + 1)) {
969 /* Any ith table columns occurring in search_cond should be
970 copied, as this condition cannot be tested already on the
971 fetch from the ith table */
973 plan = sel_node_get_nth_plan(sel_node, i);
975 opt_find_all_cols(TRUE, plan->index, &(plan->columns), plan,
976 search_cond);
980 /*******************************************************************//**
981 Classifies the table columns according to whether we use the column only while
982 holding the latch on the page, or whether we have to copy the column value to
983 dynamic memory. Puts the first occurrence of a column to either list in the
984 plan node, and puts indirections to later occurrences of the column. */
985 static
986 void
987 opt_classify_cols(
988 /*==============*/
989 sel_node_t* sel_node, /*!< in: select node */
990 ulint i) /*!< in: ith table in the join */
992 plan_t* plan;
993 que_node_t* exp;
995 plan = sel_node_get_nth_plan(sel_node, i);
997 /* The final value of the following field will depend on the
998 environment of the select statement: */
1000 plan->must_get_clust = FALSE;
1002 UT_LIST_INIT(plan->columns);
1004 /* All select list columns should be copied: therefore TRUE as the
1005 first argument */
1007 exp = sel_node->select_list;
1009 while (exp) {
1010 opt_find_all_cols(TRUE, plan->index, &(plan->columns), plan,
1011 exp);
1012 exp = que_node_get_next(exp);
1015 opt_find_copy_cols(sel_node, i, sel_node->search_cond);
1017 /* All remaining columns in the search condition are temporary
1018 columns: therefore FALSE */
1020 opt_find_all_cols(FALSE, plan->index, &(plan->columns), plan,
1021 sel_node->search_cond);
1024 /*******************************************************************//**
1025 Fills in the info in plan which is used in accessing a clustered index
1026 record. The columns must already be classified for the plan node. */
1027 static
1028 void
1029 opt_clust_access(
1030 /*=============*/
1031 sel_node_t* sel_node, /*!< in: select node */
1032 ulint n) /*!< in: nth table in select */
1034 plan_t* plan;
1035 dict_table_t* table;
1036 dict_index_t* clust_index;
1037 dict_index_t* index;
1038 mem_heap_t* heap;
1039 ulint n_fields;
1040 ulint pos;
1041 ulint i;
1043 plan = sel_node_get_nth_plan(sel_node, n);
1045 index = plan->index;
1047 /* The final value of the following field depends on the environment
1048 of the select statement: */
1050 plan->no_prefetch = FALSE;
1052 if (dict_index_is_clust(index)) {
1053 plan->clust_map = NULL;
1054 plan->clust_ref = NULL;
1056 return;
1059 table = index->table;
1061 clust_index = dict_table_get_first_index(table);
1063 n_fields = dict_index_get_n_unique(clust_index);
1065 heap = pars_sym_tab_global->heap;
1067 plan->clust_ref = dtuple_create(heap, n_fields);
1069 dict_index_copy_types(plan->clust_ref, clust_index, n_fields);
1071 plan->clust_map = mem_heap_alloc(heap, n_fields * sizeof(ulint));
1073 for (i = 0; i < n_fields; i++) {
1074 pos = dict_index_get_nth_field_pos(index, clust_index, i);
1076 ut_a(pos != ULINT_UNDEFINED);
1078 /* We optimize here only queries to InnoDB's internal system
1079 tables, and they should not contain column prefix indexes. */
1081 if (dict_index_get_nth_field(index, pos)->prefix_len != 0
1082 || dict_index_get_nth_field(clust_index, i)
1083 ->prefix_len != 0) {
1084 fprintf(stderr,
1085 "InnoDB: Error in pars0opt.c:"
1086 " table %s has prefix_len != 0\n",
1087 index->table_name);
1090 *(plan->clust_map + i) = pos;
1092 ut_ad(pos != ULINT_UNDEFINED);
1096 /*******************************************************************//**
1097 Optimizes a select. Decides which indexes to tables to use. The tables
1098 are accessed in the order that they were written to the FROM part in the
1099 select statement. */
1100 UNIV_INTERN
1101 void
1102 opt_search_plan(
1103 /*============*/
1104 sel_node_t* sel_node) /*!< in: parsed select node */
1106 sym_node_t* table_node;
1107 dict_table_t* table;
1108 order_node_t* order_by;
1109 ulint i;
1111 sel_node->plans = mem_heap_alloc(pars_sym_tab_global->heap,
1112 sel_node->n_tables * sizeof(plan_t));
1114 /* Analyze the search condition to find out what we know at each
1115 join stage about the conditions that the columns of a table should
1116 satisfy */
1118 table_node = sel_node->table_list;
1120 if (sel_node->order_by == NULL) {
1121 sel_node->asc = TRUE;
1122 } else {
1123 order_by = sel_node->order_by;
1125 sel_node->asc = order_by->asc;
1128 for (i = 0; i < sel_node->n_tables; i++) {
1130 table = table_node->table;
1132 /* Choose index through which to access the table */
1134 opt_search_plan_for_table(sel_node, i, table);
1136 /* Determine the search condition conjuncts we can test at
1137 this table; normalize the end conditions */
1139 opt_determine_and_normalize_test_conds(sel_node, i);
1141 table_node = que_node_get_next(table_node);
1144 table_node = sel_node->table_list;
1146 for (i = 0; i < sel_node->n_tables; i++) {
1148 /* Classify the table columns into those we only need to access
1149 but not copy, and to those we must copy to dynamic memory */
1151 opt_classify_cols(sel_node, i);
1153 /* Calculate possible info for accessing the clustered index
1154 record */
1156 opt_clust_access(sel_node, i);
1158 table_node = que_node_get_next(table_node);
1161 /* Check that the plan obeys a possible order-by clause: if not,
1162 an assertion error occurs */
1164 opt_check_order_by(sel_node);
1166 #ifdef UNIV_SQL_DEBUG
1167 opt_print_query_plan(sel_node);
1168 #endif
1171 /********************************************************************//**
1172 Prints info of a query plan. */
1173 UNIV_INTERN
1174 void
1175 opt_print_query_plan(
1176 /*=================*/
1177 sel_node_t* sel_node) /*!< in: select node */
1179 plan_t* plan;
1180 ulint n_fields;
1181 ulint i;
1183 fputs("QUERY PLAN FOR A SELECT NODE\n", stderr);
1185 fputs(sel_node->asc ? "Asc. search; " : "Desc. search; ", stderr);
1187 if (sel_node->set_x_locks) {
1188 fputs("sets row x-locks; ", stderr);
1189 ut_a(sel_node->row_lock_mode == LOCK_X);
1190 ut_a(!sel_node->consistent_read);
1191 } else if (sel_node->consistent_read) {
1192 fputs("consistent read; ", stderr);
1193 } else {
1194 ut_a(sel_node->row_lock_mode == LOCK_S);
1195 fputs("sets row s-locks; ", stderr);
1198 putc('\n', stderr);
1200 for (i = 0; i < sel_node->n_tables; i++) {
1201 plan = sel_node_get_nth_plan(sel_node, i);
1203 if (plan->tuple) {
1204 n_fields = dtuple_get_n_fields(plan->tuple);
1205 } else {
1206 n_fields = 0;
1209 fputs("Table ", stderr);
1210 dict_index_name_print(stderr, NULL, plan->index);
1211 fprintf(stderr,"; exact m. %lu, match %lu, end conds %lu\n",
1212 (unsigned long) plan->n_exact_match,
1213 (unsigned long) n_fields,
1214 (unsigned long) UT_LIST_GET_LEN(plan->end_conds));