1 /******************************************************
6 Created 12/21/1997 Heikki Tuuri
7 *******************************************************/
12 #include "pars0opt.ic"
18 #include "dict0dict.h"
22 #include "pars0pars.h"
23 #include "lock0lock.h"
25 #define OPT_EQUAL 1 /* comparison by = */
26 #define OPT_COMPARISON 2 /* comparison by <, >, <=, or >= */
28 #define OPT_NOT_COND 1
29 #define OPT_END_COND 2
30 #define OPT_TEST_COND 3
31 #define OPT_SCROLL_COND 4
34 /***********************************************************************
35 Inverts a comparison operator. */
40 /* out: the equivalent operator when the order of
41 the arguments is switched */
42 int op
) /* in: operator */
46 } else if (op
== '>') {
48 } else if (op
== '=') {
50 } else if (op
== PARS_LE_TOKEN
) {
51 return(PARS_GE_TOKEN
);
52 } else if (op
== PARS_GE_TOKEN
) {
53 return(PARS_LE_TOKEN
);
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. */
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 */
74 func_node_t
* func_node
;
80 ut_ad(exp
&& sel_node
);
82 if (que_node_get_type(exp
) == QUE_NODE_FUNC
) {
85 arg
= func_node
->args
;
88 if (!opt_check_exp_determined_before(arg
, sel_node
,
93 arg
= que_node_get_next(arg
);
99 ut_a(que_node_get_type(exp
) == QUE_NODE_SYMBOL
);
103 if (sym_node
->token_type
!= SYM_COLUMN
) {
108 for (i
= 0; i
< nth_table
; i
++) {
110 table
= sel_node_get_nth_plan(sel_node
, i
)->table
;
112 if (sym_node
->table
== table
) {
121 /***********************************************************************
122 Looks in a comparison condition if a column value is already restricted by
123 it BEFORE the nth table is accessed. */
126 opt_look_for_col_in_comparison_before(
127 /*==================================*/
128 /* out: expression restricting the
129 value of the column, or NULL if not
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
138 ulint
* op
) /* out: comparison operator ('=',
139 PARS_GE_TOKEN, ... ); this is inverted
140 if the column appears on the right
143 sym_node_t
* sym_node
;
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
));
156 table
= sel_node_get_nth_plan(sel_node
, nth_table
)->table
;
158 if ((cmp_type
== OPT_EQUAL
) && (search_cond
->func
!= '=')) {
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
)) {
171 arg
= search_cond
->args
;
173 if (que_node_get_type(arg
) == QUE_NODE_SYMBOL
) {
176 if ((sym_node
->token_type
== SYM_COLUMN
)
177 && (sym_node
->table
== table
)
178 && (sym_node
->col_no
== col_no
)) {
180 /* sym_node contains the desired column id */
182 /* Check if the expression on the right side of the
183 operator is already determined */
185 exp
= que_node_get_next(arg
);
187 if (opt_check_exp_determined_before(exp
, sel_node
,
189 *op
= search_cond
->func
;
196 exp
= search_cond
->args
;
197 arg
= que_node_get_next(arg
);
199 if (que_node_get_type(arg
) == QUE_NODE_SYMBOL
) {
202 if ((sym_node
->token_type
== SYM_COLUMN
)
203 && (sym_node
->table
== table
)
204 && (sym_node
->col_no
== col_no
)) {
206 if (opt_check_exp_determined_before(exp
, sel_node
,
208 *op
= opt_invert_cmp_op(search_cond
->func
);
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. */
225 opt_look_for_col_in_cond_before(
226 /*============================*/
227 /* out: expression restricting the
228 value of the column, or NULL if not
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
237 ulint
* op
) /* out: comparison operator ('=',
238 PARS_GE_TOKEN, ... ) */
240 func_node_t
* new_cond
;
243 if (search_cond
== NULL
) {
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
);
252 if (search_cond
->func
== PARS_AND_TOKEN
) {
253 new_cond
= search_cond
->args
;
255 exp
= opt_look_for_col_in_cond_before(cmp_type
, col_no
,
263 new_cond
= que_node_get_next(new_cond
);
265 exp
= opt_look_for_col_in_cond_before(cmp_type
, col_no
,
271 exp
= opt_look_for_col_in_comparison_before(cmp_type
, col_no
,
272 search_cond
, sel_node
,
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
283 if (sel_node
->asc
&& ((*op
== '<') || (*op
== PARS_LE_TOKEN
))) {
287 } else if (!sel_node
->asc
288 && ((*op
== '>') || (*op
== PARS_GE_TOKEN
))) {
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
305 opt_calc_index_goodness(
306 /*====================*/
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
313 ulint
* last_op
) /* out: last comparison operator, if
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. */
330 n_fields
= dict_index_get_n_unique_in_tree(index
);
332 for (j
= 0; j
< n_fields
; j
++) {
334 col_no
= dict_index_get_nth_col_no(index
, j
);
336 exp
= opt_look_for_col_in_cond_before(
337 OPT_EQUAL
, col_no
, sel_node
->search_cond
,
338 sel_node
, nth_table
, &op
);
340 /* The value for this column is exactly known already
341 at this stage of the join */
347 /* Look for non-equality comparisons */
349 exp
= opt_look_for_col_in_cond_before(
350 OPT_COMPARISON
, col_no
, sel_node
->search_cond
,
351 sel_node
, nth_table
, &op
);
362 if (goodness
>= 4 * dict_index_get_n_unique(index
)) {
365 if (index
->type
& DICT_CLUSTERED
) {
371 /* We have to test for goodness here, as last_op may note be set */
372 if (goodness
&& index
->type
& DICT_CLUSTERED
) {
380 /***********************************************************************
381 Calculates the number of matched fields based on an index goodness. */
384 opt_calc_n_fields_from_goodness(
385 /*============================*/
386 /* out: number of excatly or partially matched
388 ulint goodness
) /* in: goodness */
390 return(((goodness
% 1024) + 2) / 4);
393 /***********************************************************************
394 Converts a comparison operator to the corresponding search mode PAGE_CUR_GE,
398 opt_op_to_search_mode(
399 /*==================*/
400 /* out: search mode */
401 ibool asc
, /* in: TRUE if the rows should be fetched in an
403 ulint op
) /* in: operator '=', PARS_GE_TOKEN, ... */
411 } else if (op
== '<') {
414 } else if (op
== '>') {
417 } else if (op
== PARS_GE_TOKEN
) {
420 } else if (op
== PARS_LE_TOKEN
) {
430 /***********************************************************************
431 Determines if a node is an argument node of a function node. */
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 */
442 arg
= func_node
->args
;
445 if (arg
== arg_node
) {
450 arg
= que_node_get_next(arg
);
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
464 sel_node_t
* sel_node
) /* in: select node; asserts an error
465 if the plan does not agree with the
468 order_node_t
* order_node
;
469 dict_table_t
* order_table
;
474 if (!sel_node
->order_by
) {
479 order_node
= sel_node
->order_by
;
480 order_col_no
= order_node
->column
->col_no
;
481 order_table
= order_node
->column
->table
;
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 */
489 for (i
= 0; i
< sel_node
->n_tables
; i
++) {
491 plan
= sel_node_get_nth_plan(sel_node
, i
);
493 if (i
< sel_node
->n_tables
- 1) {
494 ut_a(dict_index_get_n_unique(plan
->index
)
495 <= plan
->n_exact_match
);
497 ut_a(plan
->table
== order_table
);
499 ut_a((dict_index_get_n_unique(plan
->index
)
500 <= plan
->n_exact_match
)
501 || (dict_index_get_nth_col_no(plan
->index
,
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
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 */
522 dict_index_t
* best_index
;
525 ulint last_op
= 75946965; /* Eliminate a Purify
528 ulint best_last_op
= 0; /* remove warning */
529 que_node_t
* index_plan
[256];
530 que_node_t
* best_index_plan
[256];
532 plan
= sel_node_get_nth_plan(sel_node
, i
);
535 plan
->asc
= sel_node
->asc
;
536 plan
->pcur_is_open
= FALSE
;
537 plan
->cursor_at_end
= FALSE
;
539 /* Calculate goodness for each index of the table */
541 index
= dict_table_get_first_index(table
);
542 best_index
= index
; /* Eliminate compiler warning */
545 /* should be do ... until ? comment by Jani */
547 goodness
= opt_calc_index_goodness(index
, sel_node
, i
,
548 index_plan
, &last_op
);
549 if (goodness
> best_goodness
) {
552 best_goodness
= goodness
;
553 n_fields
= opt_calc_n_fields_from_goodness(goodness
);
555 ut_memcpy(best_index_plan
, index_plan
,
556 n_fields
* sizeof(void*));
557 best_last_op
= last_op
;
560 index
= dict_table_get_next_index(index
);
563 plan
->index
= best_index
;
565 n_fields
= opt_calc_n_fields_from_goodness(best_goodness
);
569 plan
->n_exact_match
= 0;
571 plan
->tuple
= dtuple_create(pars_sym_tab_global
->heap
,
573 dict_index_copy_types(plan
->tuple
, plan
->index
, n_fields
);
575 plan
->tuple_exps
= mem_heap_alloc(pars_sym_tab_global
->heap
,
576 n_fields
* sizeof(void*));
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
;
583 plan
->n_exact_match
= n_fields
- 1;
586 plan
->mode
= opt_op_to_search_mode(sel_node
->asc
,
590 if ((best_index
->type
& DICT_CLUSTERED
)
591 && (plan
->n_exact_match
>= dict_index_get_n_unique(best_index
))) {
593 plan
->unique_search
= TRUE
;
595 plan
->unique_search
= FALSE
;
598 plan
->old_vers_heap
= NULL
;
600 btr_pcur_init(&(plan
->pcur
));
601 btr_pcur_init(&(plan
->clust_pcur
));
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. */
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 */
626 ut_ad(cond
&& sel_node
);
628 plan
= sel_node_get_nth_plan(sel_node
, i
);
630 /* Check if the condition is determined after the ith table has been
631 accessed, but not after the i - 1:th */
633 if (!opt_check_exp_determined_before(cond
, sel_node
, i
+ 1)) {
635 return(OPT_NOT_COND
);
638 if ((i
> 0) && opt_check_exp_determined_before(cond
, sel_node
, i
)) {
640 return(OPT_NOT_COND
);
643 /* If the condition is an exact match condition used in constructing
644 the search tuple, it is classified as OPT_END_COND */
647 n_fields
= dtuple_get_n_fields(plan
->tuple
);
652 for (j
= 0; j
< plan
->n_exact_match
; j
++) {
654 if (opt_is_arg(plan
->tuple_exps
[j
], cond
)) {
656 return(OPT_END_COND
);
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. */
666 if ((n_fields
> plan
->n_exact_match
)
667 && opt_is_arg(plan
->tuple_exps
[n_fields
- 1], cond
)) {
669 return(OPT_SCROLL_COND
);
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 */
677 if ((dict_index_get_n_fields(plan
->index
) > plan
->n_exact_match
)
678 && opt_look_for_col_in_comparison_before(
680 dict_index_get_nth_col_no(plan
->index
,
681 plan
->n_exact_match
),
682 cond
, sel_node
, i
, &op
)) {
684 if (sel_node
->asc
&& ((op
== '<') || (op
== PARS_LE_TOKEN
))) {
686 return(OPT_END_COND
);
689 if (!sel_node
->asc
&& ((op
== '>') || (op
== PARS_GE_TOKEN
))) {
691 return(OPT_END_COND
);
695 /* Otherwise, cond is classified as OPT_TEST_COND */
697 return(OPT_TEST_COND
);
700 /***********************************************************************
701 Recursively looks for test conditions for a table in a join. */
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 */
711 func_node_t
* new_cond
;
720 if (cond
->func
== PARS_AND_TOKEN
) {
721 new_cond
= cond
->args
;
723 opt_find_test_conds(sel_node
, i
, new_cond
);
725 new_cond
= que_node_get_next(new_cond
);
727 opt_find_test_conds(sel_node
, i
, new_cond
);
732 plan
= sel_node_get_nth_plan(sel_node
, i
);
734 class = opt_classify_comparison(sel_node
, i
, cond
);
736 if (class == OPT_END_COND
) {
737 UT_LIST_ADD_LAST(cond_list
, plan
->end_conds
, cond
);
739 } else if (class == OPT_TEST_COND
) {
740 UT_LIST_ADD_LAST(cond_list
, plan
->other_conds
, cond
);
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. */
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 */
759 sym_node_t
* sym_node
;
763 arg2
= que_node_get_next(arg1
);
765 if (que_node_get_type(arg2
) == QUE_NODE_SYMBOL
) {
769 if ((sym_node
->token_type
== SYM_COLUMN
)
770 && (sym_node
->table
== table
)) {
772 /* Switch the order of the arguments */
775 que_node_list_add_last(NULL
, arg2
);
776 que_node_list_add_last(arg2
, arg1
);
778 /* Invert the operator */
779 cond
->func
= opt_invert_cmp_op(cond
->func
);
783 cond
= UT_LIST_GET_NEXT(cond_list
, cond
);
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
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 */
800 plan
= sel_node_get_nth_plan(sel_node
, i
);
802 UT_LIST_INIT(plan
->end_conds
);
803 UT_LIST_INIT(plan
->other_conds
);
805 /* Recursively go through the conjuncts and classify them */
807 opt_find_test_conds(sel_node
, i
, sel_node
->search_cond
);
809 opt_normalize_cmp_conds(UT_LIST_GET_FIRST(plan
->end_conds
),
812 ut_a(UT_LIST_GET_LEN(plan
->end_conds
) >= plan
->n_exact_match
);
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
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
835 func_node_t
* func_node
;
837 sym_node_t
* sym_node
;
838 sym_node_t
* col_node
;
846 if (que_node_get_type(exp
) == QUE_NODE_FUNC
) {
849 arg
= func_node
->args
;
852 opt_find_all_cols(copy_val
, index
, col_list
, plan
,
854 arg
= que_node_get_next(arg
);
860 ut_a(que_node_get_type(exp
) == QUE_NODE_SYMBOL
);
864 if (sym_node
->token_type
!= SYM_COLUMN
) {
869 if (sym_node
->table
!= index
->table
) {
874 /* Look for an occurrence of the same column in the plan column
877 col_node
= UT_LIST_GET_FIRST(*col_list
);
880 if (col_node
->col_no
== sym_node
->col_no
) {
882 if (col_node
== sym_node
) {
883 /* sym_node was already in a list: do
889 /* Put an indirection */
890 sym_node
->indirection
= col_node
;
891 sym_node
->alias
= col_node
;
896 col_node
= UT_LIST_GET_NEXT(col_var_list
, col_node
);
899 /* The same column did not occur in the list: add it */
901 UT_LIST_ADD_LAST(col_var_list
, *col_list
, sym_node
);
903 sym_node
->copy_val
= copy_val
;
905 /* Fill in the field_no fields in sym_node */
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
)) {
913 col_pos
= dict_index_get_nth_col_pos(index
, sym_node
->col_no
);
915 if (col_pos
== ULINT_UNDEFINED
) {
917 plan
->must_get_clust
= TRUE
;
920 sym_node
->field_nos
[SYM_SEC_FIELD_NO
] = col_pos
;
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
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 */
937 func_node_t
* new_cond
;
940 if (search_cond
== NULL
) {
945 ut_ad(que_node_get_type(search_cond
) == QUE_NODE_FUNC
);
947 if (search_cond
->func
== PARS_AND_TOKEN
) {
948 new_cond
= search_cond
->args
;
950 opt_find_copy_cols(sel_node
, i
, new_cond
);
952 new_cond
= que_node_get_next(new_cond
);
954 opt_find_copy_cols(sel_node
, i
, new_cond
);
959 if (!opt_check_exp_determined_before(search_cond
, sel_node
, i
+ 1)) {
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 */
965 plan
= sel_node_get_nth_plan(sel_node
, i
);
967 opt_find_all_cols(TRUE
, plan
->index
, &(plan
->columns
), plan
,
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. */
981 sel_node_t
* sel_node
, /* in: select node */
982 ulint i
) /* in: ith table in the join */
987 plan
= sel_node_get_nth_plan(sel_node
, i
);
989 /* The final value of the following field will depend on the
990 environment of the select statement: */
992 plan
->must_get_clust
= FALSE
;
994 UT_LIST_INIT(plan
->columns
);
996 /* All select list columns should be copied: therefore TRUE as the
999 exp
= sel_node
->select_list
;
1002 opt_find_all_cols(TRUE
, plan
->index
, &(plan
->columns
), plan
,
1004 exp
= que_node_get_next(exp
);
1007 opt_find_copy_cols(sel_node
, i
, sel_node
->search_cond
);
1009 /* All remaining columns in the search condition are temporary
1010 columns: therefore FALSE */
1012 opt_find_all_cols(FALSE
, plan
->index
, &(plan
->columns
), plan
,
1013 sel_node
->search_cond
);
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. */
1023 sel_node_t
* sel_node
, /* in: select node */
1024 ulint n
) /* in: nth table in select */
1027 dict_table_t
* table
;
1028 dict_index_t
* clust_index
;
1029 dict_index_t
* index
;
1035 plan
= sel_node_get_nth_plan(sel_node
, n
);
1037 index
= plan
->index
;
1039 /* The final value of the following field depends on the environment
1040 of the select statement: */
1042 plan
->no_prefetch
= FALSE
;
1044 if (index
->type
& DICT_CLUSTERED
) {
1045 plan
->clust_map
= NULL
;
1046 plan
->clust_ref
= NULL
;
1051 table
= index
->table
;
1053 clust_index
= dict_table_get_first_index(table
);
1055 n_fields
= dict_index_get_n_unique(clust_index
);
1057 heap
= pars_sym_tab_global
->heap
;
1059 plan
->clust_ref
= dtuple_create(heap
, n_fields
);
1061 dict_index_copy_types(plan
->clust_ref
, clust_index
, n_fields
);
1063 plan
->clust_map
= mem_heap_alloc(heap
, n_fields
* sizeof(ulint
));
1065 for (i
= 0; i
< n_fields
; i
++) {
1066 pos
= dict_index_get_nth_field_pos(index
, clust_index
, i
);
1068 ut_a(pos
!= ULINT_UNDEFINED
);
1070 /* We optimize here only queries to InnoDB's internal system
1071 tables, and they should not contain column prefix indexes. */
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) {
1077 "InnoDB: Error in pars0opt.c:"
1078 " table %s has prefix_len != 0\n",
1082 *(plan
->clust_map
+ i
) = pos
;
1084 ut_ad(pos
!= ULINT_UNDEFINED
);
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. */
1096 sel_node_t
* sel_node
) /* in: parsed select node */
1098 sym_node_t
* table_node
;
1099 dict_table_t
* table
;
1100 order_node_t
* order_by
;
1103 sel_node
->plans
= mem_heap_alloc(pars_sym_tab_global
->heap
,
1104 sel_node
->n_tables
* sizeof(plan_t
));
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
1110 table_node
= sel_node
->table_list
;
1112 if (sel_node
->order_by
== NULL
) {
1113 sel_node
->asc
= TRUE
;
1115 order_by
= sel_node
->order_by
;
1117 sel_node
->asc
= order_by
->asc
;
1120 for (i
= 0; i
< sel_node
->n_tables
; i
++) {
1122 table
= table_node
->table
;
1124 /* Choose index through which to access the table */
1126 opt_search_plan_for_table(sel_node
, i
, table
);
1128 /* Determine the search condition conjuncts we can test at
1129 this table; normalize the end conditions */
1131 opt_determine_and_normalize_test_conds(sel_node
, i
);
1133 table_node
= que_node_get_next(table_node
);
1136 table_node
= sel_node
->table_list
;
1138 for (i
= 0; i
< sel_node
->n_tables
; i
++) {
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 */
1143 opt_classify_cols(sel_node
, i
);
1145 /* Calculate possible info for accessing the clustered index
1148 opt_clust_access(sel_node
, i
);
1150 table_node
= que_node_get_next(table_node
);
1153 /* Check that the plan obeys a possible order-by clause: if not,
1154 an assertion error occurs */
1156 opt_check_order_by(sel_node
);
1158 #ifdef UNIV_SQL_DEBUG
1159 opt_print_query_plan(sel_node
);
1163 /************************************************************************
1164 Prints info of a query plan. */
1167 opt_print_query_plan(
1168 /*=================*/
1169 sel_node_t
* sel_node
) /* in: select node */
1175 fputs("QUERY PLAN FOR A SELECT NODE\n", stderr
);
1177 fputs(sel_node
->asc
? "Asc. search; " : "Desc. search; ", stderr
);
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
);
1186 ut_a(sel_node
->row_lock_mode
== LOCK_S
);
1187 fputs("sets row s-locks; ", stderr
);
1192 for (i
= 0; i
< sel_node
->n_tables
; i
++) {
1193 plan
= sel_node_get_nth_plan(sel_node
, i
);
1196 n_fields
= dtuple_get_n_fields(plan
->tuple
);
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
));