diff options
-rw-r--r-- | include/my_bitmap.h | 2 | ||||
-rw-r--r-- | mysql-test/r/index_merge_innodb.result | 2 | ||||
-rw-r--r-- | mysql-test/r/index_merge_ror_cpk.result | 2 | ||||
-rw-r--r-- | mysql-test/t/index_merge_innodb.test | 2 | ||||
-rw-r--r-- | sql/opt_range.cc | 352 |
5 files changed, 184 insertions, 176 deletions
diff --git a/include/my_bitmap.h b/include/my_bitmap.h index fb1c3c69563..4caa3b85456 100644 --- a/include/my_bitmap.h +++ b/include/my_bitmap.h @@ -24,7 +24,7 @@ typedef struct st_bitmap { uchar *bitmap; - uint bitmap_size; + uint bitmap_size; /* number of bytes occupied by the above */ /* mutex will be acquired for the duration of each bitmap operation if thread_safe flag in bitmap_init was set. Otherwise, we optimize by not diff --git a/mysql-test/r/index_merge_innodb.result b/mysql-test/r/index_merge_innodb.result index d1914844839..4c6be698749 100644 --- a/mysql-test/r/index_merge_innodb.result +++ b/mysql-test/r/index_merge_innodb.result @@ -1,4 +1,4 @@ -drop table if exists t1; +drop table if exists t1,t2; create table t1 ( key1 int not null, diff --git a/mysql-test/r/index_merge_ror_cpk.result b/mysql-test/r/index_merge_ror_cpk.result index ba8efe42bd6..c344cef5db9 100644 --- a/mysql-test/r/index_merge_ror_cpk.result +++ b/mysql-test/r/index_merge_ror_cpk.result @@ -26,7 +26,7 @@ primary key (pk1, pk2) ) engine=innodb; explain select * from t1 where pk1 = 1 and pk2 < 80 and key1=0; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,key1 PRIMARY 4 const 1 Using where +1 SIMPLE t1 range PRIMARY,key1 PRIMARY 8 NULL 9 Using where select * from t1 where pk1 = 1 and pk2 < 80 and key1=0; pk1 pk2 key1 key2 pktail1ok pktail2ok pktail3bad pktail4bad pktail5bad pk2copy badkey filler1 filler2 1 10 0 0 0 0 0 0 0 10 0 filler-data-10 filler2 diff --git a/mysql-test/t/index_merge_innodb.test b/mysql-test/t/index_merge_innodb.test index cc1a3169d0e..5e270c161a2 100644 --- a/mysql-test/t/index_merge_innodb.test +++ b/mysql-test/t/index_merge_innodb.test @@ -4,7 +4,7 @@ -- source include/have_innodb.inc --disable_warnings -drop table if exists t1; +drop table if exists t1,t2; --enable_warnings create table t1 diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 4b0e5f036cb..6be23835032 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -50,7 +50,7 @@ #define test_use_count(A) {} #endif - +#define double2rows(x) ((ha_rows)(x)) static int sel_cmp(Field *f,char *a,char *b,uint8 a_flag,uint8 b_flag); static char is_null_string[2]= {1,0}; @@ -1628,6 +1628,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", keys_to_use.to_ulonglong(), (ulong) prev_tables, (ulong) const_tables)); + DBUG_PRINT("info", ("records=%lu", (ulong)head->file->records)); delete quick; quick=0; needed_reg.clear_all(); @@ -1874,6 +1875,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, double get_sweep_read_cost(const PARAM *param, ha_rows records) { double result; + DBUG_ENTER("get_sweep_read_cost"); if (param->table->file->primary_key_is_clustered()) { result= param->table->file->read_time(param->table->s->primary_key, @@ -1912,7 +1914,7 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) } } DBUG_PRINT("info",("returning cost=%g", result)); - return result; + DBUG_RETURN(result); } @@ -2393,43 +2395,27 @@ typedef struct { const PARAM *param; MY_BITMAP covered_fields; /* union of fields covered by all scans */ - - /* TRUE if covered_fields is a superset of needed_fields */ - bool is_covering; - - double index_scan_costs; /* SUM(cost of 'index-only' scans) */ - double total_cost; /* Fraction of table records that satisfies conditions of all scans. This is the number of full records that will be retrieved if a non-index_only index intersection will be employed. */ - double records_fract; + double out_rows; + /* TRUE if covered_fields is a superset of needed_fields */ + bool is_covering; + ha_rows index_records; /* sum(#records to look in indexes) */ + double index_scan_costs; /* SUM(cost of 'index-only' scans) */ + double total_cost; } ROR_INTERSECT_INFO; /* - Re-initialize an allocated intersect info to contain zero scans. - SYNOPSIS - info Intersection info structure to re-initialize. -*/ - -static void ror_intersect_reinit(ROR_INTERSECT_INFO *info) -{ - info->is_covering= FALSE; - info->index_scan_costs= 0.0f; - info->records_fract= 1.0f; - bitmap_clear_all(&info->covered_fields); -} - -/* Allocate a ROR_INTERSECT_INFO and initialize it to contain zero scans. SYNOPSIS ror_intersect_init() param Parameter from test_quick_select - is_index_only If TRUE, set ROR_INTERSECT_INFO to be covering RETURN allocated structure @@ -2437,7 +2423,7 @@ static void ror_intersect_reinit(ROR_INTERSECT_INFO *info) */ static -ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only) +ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param) { ROR_INTERSECT_INFO *info; uchar* buf; @@ -2450,46 +2436,39 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only) if (bitmap_init(&info->covered_fields, buf, param->fields_bitmap_size*8, FALSE)) return NULL; - ror_intersect_reinit(info); + info->is_covering= FALSE; + info->index_scan_costs= 0.0f; + info->index_records= 0; + info->out_rows= param->table->file->records; + bitmap_clear_all(&info->covered_fields); return info; } - +void ror_intersect_cpy(ROR_INTERSECT_INFO *dst, const ROR_INTERSECT_INFO *src) +{ + dst->param= src->param; + memcpy(dst->covered_fields.bitmap, src->covered_fields.bitmap, + src->covered_fields.bitmap_size); + dst->out_rows= src->out_rows; + dst->is_covering= src->is_covering; + dst->index_records= src->index_records; + dst->index_scan_costs= src->index_scan_costs; + dst->total_cost= src->total_cost; +} /* - Check if adding a ROR scan to a ROR-intersection reduces its cost of - ROR-intersection and if yes, update parameters of ROR-intersection, - including its cost. + Get selectivity of a ROR scan wrt ROR-intersection. SYNOPSIS - ror_intersect_add() - param Parameter from test_quick_select - info ROR-intersection structure to add the scan to. - ror_scan ROR scan info to add. - is_cpk_scan If TRUE, add the scan as CPK scan (this can be inferred - from other parameters and is passed separately only to - avoid duplicating the inference code) - + ror_scan_selectivity() + info ROR-interection + scan ROR scan + NOTES - Adding a ROR scan to ROR-intersect "makes sense" iff the cost of ROR- - intersection decreases. The cost of ROR-intersection is caclulated as - follows: - - cost= SUM_i(key_scan_cost_i) + cost_of_full_rows_retrieval - - if (union of indexes used covers all needed fields) - cost_of_full_rows_retrieval= 0; - else - { - cost_of_full_rows_retrieval= - cost_of_sweep_read(E(rows_to_retrieve), rows_in_table); - } - - E(rows_to_retrieve) is caclulated as follows: Suppose we have a condition on several keys cond=k_11=c_11 AND k_12=c_12 AND ... // parts of first key k_21=c_21 AND k_22=c_22 AND ... // parts of second key ... - k_n1=c_n1 AND k_n3=c_n3 AND ... (1) + k_n1=c_n1 AND k_n3=c_n3 AND ... (1) //parts of the key used by *scan where k_ij may be the same as any k_pq (i.e. keys may have common parts). @@ -2563,38 +2542,30 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only) and reduce adjacent fractions. RETURN - TRUE ROR scan added to ROR-intersection, cost updated. - FALSE It doesn't make sense to add this ROR scan to this ROR-intersection. + Selectivity of given ROR scan. + */ -bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, - ROR_SCAN_INFO* ror_scan, bool is_cpk_scan=FALSE) +static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info, + const ROR_SCAN_INFO *scan) { - int i; - SEL_ARG *sel_arg; - KEY_PART_INFO *key_part= - info->param->table->key_info[ror_scan->keynr].key_part; double selectivity_mult= 1.0; + KEY_PART_INFO *key_part= info->param->table->key_info[scan->keynr].key_part; byte key_val[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; /* key values tuple */ - - DBUG_ENTER("ror_intersect_add"); - DBUG_PRINT("info", ("Current selectivity= %g", info->records_fract)); - DBUG_PRINT("info", ("Adding scan on %s", - info->param->table->key_info[ror_scan->keynr].name)); - SEL_ARG *tuple_arg= NULL; char *key_ptr= (char*) key_val; - bool cur_covered, prev_covered= - bitmap_is_set(&info->covered_fields, key_part->fieldnr); - - ha_rows prev_records= param->table->file->records; + SEL_ARG *sel_arg, *tuple_arg= NULL; + bool cur_covered; + bool prev_covered= bitmap_is_set(&info->covered_fields, key_part->fieldnr); key_range min_range; key_range max_range; min_range.key= (byte*) key_val; min_range.flag= HA_READ_KEY_EXACT; max_range.key= (byte*) key_val; max_range.flag= HA_READ_AFTER_KEY; - - for(i= 0, sel_arg= ror_scan->sel_arg; sel_arg; + ha_rows prev_records= info->param->table->file->records; + int i; + DBUG_ENTER("ror_intersect_selectivity"); + for(i= 0, sel_arg= scan->sel_arg; sel_arg; i++, sel_arg= sel_arg->next_key_part) { cur_covered= bitmap_is_set(&info->covered_fields, (key_part + i)->fieldnr); @@ -2604,7 +2575,7 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, { if (!tuple_arg) { - tuple_arg= ror_scan->sel_arg; + tuple_arg= scan->sel_arg; tuple_arg->store_min(key_part->length, &key_ptr, 0); } while (tuple_arg->next_key_part != sel_arg) @@ -2615,10 +2586,8 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, } ha_rows records; min_range.length= max_range.length= ((char*) key_ptr - (char*) key_val); - records= param->table->file-> - records_in_range(ror_scan->keynr, - &min_range, - &max_range); + records= info->param->table->file-> + records_in_range(scan->keynr, &min_range, &max_range); if (cur_covered) { /* uncovered -> covered */ @@ -2637,52 +2606,108 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, } if (!prev_covered) { - double tmp= rows2double(param->table->quick_rows[ror_scan->keynr]) / + double tmp= rows2double(info->param->table->quick_rows[scan->keynr]) / rows2double(prev_records); DBUG_PRINT("info", ("Selectivity multiplier: %g", tmp)); selectivity_mult *= tmp; } + DBUG_PRINT("info", ("Returning multiplier: %g", selectivity_mult)); + DBUG_RETURN(selectivity_mult); +} + +/* + Check if adding a ROR scan to a ROR-intersection reduces its cost of + ROR-intersection and if yes, update parameters of ROR-intersection, + including its cost. + + SYNOPSIS + ror_intersect_add() + param Parameter from test_quick_select + info ROR-intersection structure to add the scan to. + ror_scan ROR scan info to add. + is_cpk_scan If TRUE, add the scan as CPK scan (this can be inferred + from other parameters and is passed separately only to + avoid duplicating the inference code) + + NOTES + Adding a ROR scan to ROR-intersect "makes sense" iff the cost of ROR- + intersection decreases. The cost of ROR-intersection is calculated as + follows: + + cost= SUM_i(key_scan_cost_i) + cost_of_full_rows_retrieval + + When we add a scan the first increases and the second decreases. + + cost_of_full_rows_retrieval= + (union of indexes used covers all needed fields) ? + cost_of_sweep_read(E(rows_to_retrieve), rows_in_table) : + 0 + + E(rows_to_retrieve) = #rows_in_table * ror_scan_selectivity(null, scan1) * + ror_scan_selectivity({scan1}, scan2) * ... * + ror_scan_selectivity({scan1,...}, scanN). + RETURN + TRUE ROR scan added to ROR-intersection, cost updated. + FALSE It doesn't make sense to add this ROR scan to this ROR-intersection. +*/ + +static bool ror_intersect_add(ROR_INTERSECT_INFO *info, + ROR_SCAN_INFO* ror_scan, bool is_cpk_scan) +{ + double selectivity_mult= 1.0; + + DBUG_ENTER("ror_intersect_add"); + DBUG_PRINT("info", ("Current out_rows= %g", info->out_rows)); + DBUG_PRINT("info", ("Adding scan on %s", + info->param->table->key_info[ror_scan->keynr].name)); + DBUG_PRINT("info", ("is_cpk_scan=%d",is_cpk_scan)); + selectivity_mult = ror_scan_selectivity(info, ror_scan); if (selectivity_mult == 1.0) { /* Don't add this scan if it doesn't improve selectivity. */ DBUG_PRINT("info", ("The scan doesn't improve selectivity.")); - DBUG_RETURN(FALSE); + DBUG_RETURN(false); } - - info->records_fract *= selectivity_mult; - ha_rows cur_scan_records= info->param->table->quick_rows[ror_scan->keynr]; + + info->out_rows *= selectivity_mult; + DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost)); + if (is_cpk_scan) { - info->index_scan_costs += rows2double(cur_scan_records)* + /* + CPK scan is used to filter out rows. We apply filtering for + each record of every scan. Assuming 1/TIME_FOR_COMPARE_ROWID + per check this gives us: + */ + info->index_scan_costs += rows2double(info->index_records) / TIME_FOR_COMPARE_ROWID; } else { - info->index_records += cur_scan_records; + info->index_records += info->param->table->quick_rows[ror_scan->keynr]; info->index_scan_costs += ror_scan->index_read_cost; bitmap_union(&info->covered_fields, &ror_scan->covered_fields); - } - - if (!info->is_covering && bitmap_is_subset(&info->param->needed_fields, - &info->covered_fields)) - { - DBUG_PRINT("info", ("ROR-intersect is covering now")); - info->is_covering= TRUE; + if (!info->is_covering && bitmap_is_subset(&info->param->needed_fields, + &info->covered_fields)) + { + DBUG_PRINT("info", ("ROR-intersect is covering now")); + info->is_covering= TRUE; + } } info->total_cost= info->index_scan_costs; + DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost)); if (!info->is_covering) { - ha_rows table_recs= info->param->table->file->records; - info->total_cost += - get_sweep_read_cost(info->param, - (ha_rows)(info->records_fract*table_recs)); + info->total_cost += + get_sweep_read_cost(info->param, double2rows(info->out_rows)); + DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost)); } - DBUG_PRINT("info", ("New selectivity= %g", info->records_fract)); + DBUG_PRINT("info", ("New out_rows= %g", info->out_rows)); DBUG_PRINT("info", ("New cost= %g, %scovering", info->total_cost, info->is_covering?"" : "non-")); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } @@ -2701,9 +2726,13 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, a covering ROR-intersection) NOTES - get_key_scans_params must be called before for the same SEL_TREE before - this function can be called. + get_key_scans_params must be called before this function can be called. + + When this function is called by ROR-union construction algorithm it + assumes it is building an uncovered ROR-intersection (and thus # of full + records to be retrieved is wrong here). This is a hack. + IMPLEMENTATION The approximate best non-covering plan search algorithm is as follows: find_min_ror_intersection_scan() @@ -2717,11 +2746,11 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, min_scan= make_scan(S); while (R is not empty) { - if (!selectivity(S + first(R) < selectivity(S))) + firstR= R - first(R); + if (!selectivity(S + firstR < selectivity(S))) continue; - + S= S + first(R); - R= R - first(R); if (cost(S) < min_cost) { min_cost= cost(S); @@ -2752,15 +2781,15 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, bool *are_all_covering) { uint idx; - double min_cost= read_time; + double min_cost= DBL_MAX; DBUG_ENTER("get_best_ror_intersect"); if ((tree->n_ror_scans < 2) || !param->table->file->records) DBUG_RETURN(NULL); /* - Collect ROR-able SEL_ARGs and create ROR_SCAN_INFO for each of them. - Also find and save clustered PK scan if there is one. + Step1: Collect ROR-able SEL_ARGs and create ROR_SCAN_INFO for each of + them. Also find and save clustered PK scan if there is one. */ ROR_SCAN_INFO **cur_ror_scan; ROR_SCAN_INFO *cpk_scan= NULL; @@ -2796,8 +2825,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, tree->ror_scans_end);); /* Ok, [ror_scans, ror_scans_end) is array of ptrs to initialized - ROR_SCAN_INFOs. - Now, get a minimal key scan using an approximate algorithm. + ROR_SCAN_INFO's. + Step 2: Get best ROR-intersection using an approximate algorithm. */ qsort(tree->ror_scans, tree->n_ror_scans, sizeof(ROR_SCAN_INFO*), (qsort_cmp)cmp_ror_scan_info); @@ -2814,45 +2843,41 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, intersect_scans_end= intersect_scans; /* Create and incrementally update ROR intersection. */ - ROR_INTERSECT_INFO *intersect; - if (!(intersect= ror_intersect_init(param, FALSE))) + ROR_INTERSECT_INFO *intersect, *intersect_best; + if (!(intersect= ror_intersect_init(param)) || + !(intersect_best= ror_intersect_init(param))) return NULL; - /* [intersect_scans, intersect_scans_best) will hold the best combination */ + /* [intersect_scans,intersect_scans_best) will hold the best intersection */ ROR_SCAN_INFO **intersect_scans_best; - ha_rows best_rows; - bool is_best_covering; - double best_index_scan_costs; - LINT_INIT(best_rows); /* protected by intersect_scans_best */ - LINT_INIT(is_best_covering); - LINT_INIT(best_index_scan_costs); - cur_ror_scan= tree->ror_scans; - /* Start with one scan */ intersect_scans_best= intersect_scans; while (cur_ror_scan != tree->ror_scans_end && !intersect->is_covering) { - /* S= S + first(R); */ - if (ror_intersect_add(param, intersect, *cur_ror_scan)) - *(intersect_scans_end++)= *cur_ror_scan; - /* R= R - first(R); */ - cur_ror_scan++; + /* S= S + first(R); R= R - first(R); */ + if (!ror_intersect_add(intersect, *cur_ror_scan, false)) + { + cur_ror_scan++; + continue; + } + + *(intersect_scans_end++)= *(cur_ror_scan++); if (intersect->total_cost < min_cost) { /* Local minimum found, save it */ - min_cost= intersect->total_cost; - best_rows= (ha_rows)(intersect->records_fract* - rows2double(param->table->file->records)); - /* Prevent divisons by zero */ - if (!best_rows) - best_rows= 1; - is_best_covering= intersect->is_covering; + ror_intersect_cpy(intersect_best, intersect); intersect_scans_best= intersect_scans_end; - best_index_scan_costs= intersect->index_scan_costs; + min_cost = intersect->total_cost; } } + if (intersect_scans_best == intersect_scans) + { + DBUG_PRINT("info", ("None of scans increase selectivity")); + DBUG_RETURN(NULL); + } + DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "best ROR-intersection", intersect_scans, @@ -2860,48 +2885,26 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, *are_all_covering= intersect->is_covering; uint best_num= intersect_scans_best - intersect_scans; + ror_intersect_cpy(intersect, intersect_best); + /* Ok, found the best ROR-intersection of non-CPK key scans. - Check if we should add a CPK scan. - - If the obtained ROR-intersection is covering, it doesn't make sense - to add CPK scan - Clustered PK contains all fields and if we're doing - CPK scan doing other CPK scans will only add more overhead. + Check if we should add a CPK scan. If the obtained ROR-intersection is + covering, it doesn't make sense to add CPK scan. */ if (cpk_scan && !intersect->is_covering) { - /* - Handle the special case: ROR-intersect(PRIMARY, key1) is - the best, but cost(range(key1)) > cost(best_non_ror_range_scan) - */ - if (best_num == 0) - { - cur_ror_scan= tree->ror_scans; - intersect_scans_end= intersect_scans; - ror_intersect_reinit(intersect); - if (!ror_intersect_add(param, intersect, *cur_ror_scan)) - DBUG_RETURN(NULL); /* shouldn't happen actually */ - *(intersect_scans_end++)= *cur_ror_scan; - best_num++; - } - - if (ror_intersect_add(param, intersect, cpk_scan)) + if (ror_intersect_add(intersect, cpk_scan, true) && + (intersect->total_cost < min_cost)) { cpk_scan_used= TRUE; - min_cost= intersect->total_cost; - best_rows= (ha_rows)(intersect->records_fract* - rows2double(param->table->file->records)); - /* Prevent divisons by zero */ - if (!best_rows) - best_rows= 1; - is_best_covering= intersect->is_covering; - best_index_scan_costs= intersect->index_scan_costs; + intersect_best= intersect; //just set pointer here } } /* Ok, return ROR-intersect plan if we have found one */ TRP_ROR_INTERSECT *trp= NULL; - if (best_num > 1 || cpk_scan_used) + if (min_cost < read_time && (cpk_scan_used || best_num > 1)) { if (!(trp= new (param->mem_root) TRP_ROR_INTERSECT)) DBUG_RETURN(trp); @@ -2911,14 +2914,18 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, DBUG_RETURN(NULL); memcpy(trp->first_scan, intersect_scans, best_num*sizeof(ROR_SCAN_INFO*)); trp->last_scan= trp->first_scan + best_num; - trp->is_covering= is_best_covering; - trp->read_cost= min_cost; + trp->is_covering= intersect_best->is_covering; + trp->read_cost= intersect_best->total_cost; + /* Prevent divisons by zero */ + ha_rows best_rows = double2rows(intersect_best->out_rows); + if (!best_rows) + best_rows= 1; trp->records= best_rows; - trp->index_scan_costs= best_index_scan_costs; - trp->cpk_scan= cpk_scan; - DBUG_PRINT("info", - ("Returning non-covering ROR-intersect plan: cost %g, records %lu", - trp->read_cost, (ulong) trp->records)); + trp->index_scan_costs= intersect_best->index_scan_costs; + trp->cpk_scan= cpk_scan_used? cpk_scan: NULL; + DBUG_PRINT("info", ("Returning non-covering ROR-intersect plan:" + "cost %g, records %lu", + trp->read_cost, (ulong) trp->records)); } DBUG_RETURN(trp); } @@ -3145,8 +3152,9 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, found_records) + cpu_cost; - DBUG_PRINT("info",("read_time: %g found_read_time: %g", - read_time, found_read_time)); + DBUG_PRINT("info",("key %s: found_read_time: %g (cur. read_time: %g)", + param->table->key_info[keynr].name, found_read_time, + read_time)); if (read_time > found_read_time && found_records != HA_POS_ERROR /*|| read_time == DBL_MAX*/ ) |