Commit abca4cd5 authored by unknown's avatar unknown

Fixes in opt_range.cc: ROR plan choice code

 * Removed unused parameters 
 * Fixed several cost calculation errors in ror_intersect_add
 * Better code structure for ror_intersect_add and get_best_ror_intersect


include/my_bitmap.h:
  Comments added
mysql-test/r/index_merge_innodb.result:
  Test results updated
mysql-test/r/index_merge_ror_cpk.result:
  Test results updated.
mysql-test/t/index_merge_innodb.test:
  Drop all tables after use
sql/opt_range.cc:
  Fixes in ROR plan choice code
  * Removed unused parameters 
  * Fixed several cost calculation errors in ror_intersect_add
  * Better code structure for ror_intersect_add and get_best_ror_intersect
parent cd1715a2
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
typedef struct st_bitmap typedef struct st_bitmap
{ {
uchar *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 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 thread_safe flag in bitmap_init was set. Otherwise, we optimize by not
......
drop table if exists t1; drop table if exists t1,t2;
create table t1 create table t1
( (
key1 int not null, key1 int not null,
......
...@@ -26,7 +26,7 @@ primary key (pk1, pk2) ...@@ -26,7 +26,7 @@ primary key (pk1, pk2)
) engine=innodb; ) engine=innodb;
explain select * from t1 where pk1 = 1 and pk2 < 80 and key1=0; 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 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; 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 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 1 10 0 0 0 0 0 0 0 10 0 filler-data-10 filler2
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
-- source include/have_innodb.inc -- source include/have_innodb.inc
--disable_warnings --disable_warnings
drop table if exists t1; drop table if exists t1,t2;
--enable_warnings --enable_warnings
create table t1 create table t1
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
#define test_use_count(A) {} #define test_use_count(A) {}
#endif #endif
#define double2rows(x) ((ha_rows)(x))
static int sel_cmp(Field *f,char *a,char *b,uint8 a_flag,uint8 b_flag); static int sel_cmp(Field *f,char *a,char *b,uint8 a_flag,uint8 b_flag);
static char is_null_string[2]= {1,0}; 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, ...@@ -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", DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
keys_to_use.to_ulonglong(), (ulong) prev_tables, keys_to_use.to_ulonglong(), (ulong) prev_tables,
(ulong) const_tables)); (ulong) const_tables));
DBUG_PRINT("info", ("records=%lu", (ulong)head->file->records));
delete quick; delete quick;
quick=0; quick=0;
needed_reg.clear_all(); needed_reg.clear_all();
...@@ -1874,6 +1875,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, ...@@ -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 get_sweep_read_cost(const PARAM *param, ha_rows records)
{ {
double result; double result;
DBUG_ENTER("get_sweep_read_cost");
if (param->table->file->primary_key_is_clustered()) if (param->table->file->primary_key_is_clustered())
{ {
result= param->table->file->read_time(param->table->s->primary_key, 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) ...@@ -1912,7 +1914,7 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records)
} }
} }
DBUG_PRINT("info",("returning cost=%g", result)); DBUG_PRINT("info",("returning cost=%g", result));
return result; DBUG_RETURN(result);
} }
...@@ -2393,43 +2395,27 @@ typedef struct ...@@ -2393,43 +2395,27 @@ typedef struct
{ {
const PARAM *param; const PARAM *param;
MY_BITMAP covered_fields; /* union of fields covered by all scans */ 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. Fraction of table records that satisfies conditions of all scans.
This is the number of full records that will be retrieved if a This is the number of full records that will be retrieved if a
non-index_only index intersection will be employed. 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) */ 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; } 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. Allocate a ROR_INTERSECT_INFO and initialize it to contain zero scans.
SYNOPSIS SYNOPSIS
ror_intersect_init() ror_intersect_init()
param Parameter from test_quick_select param Parameter from test_quick_select
is_index_only If TRUE, set ROR_INTERSECT_INFO to be covering
RETURN RETURN
allocated structure allocated structure
...@@ -2437,7 +2423,7 @@ static void ror_intersect_reinit(ROR_INTERSECT_INFO *info) ...@@ -2437,7 +2423,7 @@ static void ror_intersect_reinit(ROR_INTERSECT_INFO *info)
*/ */
static 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; ROR_INTERSECT_INFO *info;
uchar* buf; uchar* buf;
...@@ -2450,46 +2436,39 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only) ...@@ -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, if (bitmap_init(&info->covered_fields, buf, param->fields_bitmap_size*8,
FALSE)) FALSE))
return NULL; 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; 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 Get selectivity of a ROR scan wrt ROR-intersection.
ROR-intersection and if yes, update parameters of ROR-intersection,
including its cost.
SYNOPSIS SYNOPSIS
ror_intersect_add() ror_scan_selectivity()
param Parameter from test_quick_select info ROR-interection
info ROR-intersection structure to add the scan to. scan ROR scan
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 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 Suppose we have a condition on several keys
cond=k_11=c_11 AND k_12=c_12 AND ... // parts of first key 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_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). 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) ...@@ -2563,38 +2542,30 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only)
and reduce adjacent fractions. and reduce adjacent fractions.
RETURN RETURN
TRUE ROR scan added to ROR-intersection, cost updated. Selectivity of given ROR scan.
FALSE It doesn't make sense to add this ROR scan to this ROR-intersection.
*/ */
bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
ROR_SCAN_INFO* ror_scan, bool is_cpk_scan=FALSE) 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; 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 */ 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; char *key_ptr= (char*) key_val;
bool cur_covered, prev_covered= SEL_ARG *sel_arg, *tuple_arg= NULL;
bitmap_is_set(&info->covered_fields, key_part->fieldnr); bool cur_covered;
bool prev_covered= bitmap_is_set(&info->covered_fields, key_part->fieldnr);
ha_rows prev_records= param->table->file->records;
key_range min_range; key_range min_range;
key_range max_range; key_range max_range;
min_range.key= (byte*) key_val; min_range.key= (byte*) key_val;
min_range.flag= HA_READ_KEY_EXACT; min_range.flag= HA_READ_KEY_EXACT;
max_range.key= (byte*) key_val; max_range.key= (byte*) key_val;
max_range.flag= HA_READ_AFTER_KEY; max_range.flag= HA_READ_AFTER_KEY;
ha_rows prev_records= info->param->table->file->records;
for(i= 0, sel_arg= ror_scan->sel_arg; sel_arg; 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) i++, sel_arg= sel_arg->next_key_part)
{ {
cur_covered= bitmap_is_set(&info->covered_fields, (key_part + i)->fieldnr); 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, ...@@ -2604,7 +2575,7 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info,
{ {
if (!tuple_arg) if (!tuple_arg)
{ {
tuple_arg= ror_scan->sel_arg; tuple_arg= scan->sel_arg;
tuple_arg->store_min(key_part->length, &key_ptr, 0); tuple_arg->store_min(key_part->length, &key_ptr, 0);
} }
while (tuple_arg->next_key_part != sel_arg) while (tuple_arg->next_key_part != sel_arg)
...@@ -2615,10 +2586,8 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, ...@@ -2615,10 +2586,8 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info,
} }
ha_rows records; ha_rows records;
min_range.length= max_range.length= ((char*) key_ptr - (char*) key_val); min_range.length= max_range.length= ((char*) key_ptr - (char*) key_val);
records= param->table->file-> records= info->param->table->file->
records_in_range(ror_scan->keynr, records_in_range(scan->keynr, &min_range, &max_range);
&min_range,
&max_range);
if (cur_covered) if (cur_covered)
{ {
/* uncovered -> covered */ /* uncovered -> covered */
...@@ -2637,52 +2606,108 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, ...@@ -2637,52 +2606,108 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info,
} }
if (!prev_covered) 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); rows2double(prev_records);
DBUG_PRINT("info", ("Selectivity multiplier: %g", tmp)); DBUG_PRINT("info", ("Selectivity multiplier: %g", tmp));
selectivity_mult *= 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) if (selectivity_mult == 1.0)
{ {
/* Don't add this scan if it doesn't improve selectivity. */ /* Don't add this scan if it doesn't improve selectivity. */
DBUG_PRINT("info", ("The scan doesn't improve selectivity.")); DBUG_PRINT("info", ("The scan doesn't improve selectivity."));
DBUG_RETURN(FALSE); DBUG_RETURN(false);
} }
info->records_fract *= selectivity_mult; info->out_rows *= selectivity_mult;
ha_rows cur_scan_records= info->param->table->quick_rows[ror_scan->keynr]; DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost));
if (is_cpk_scan) 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; TIME_FOR_COMPARE_ROWID;
} }
else 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; info->index_scan_costs += ror_scan->index_read_cost;
bitmap_union(&info->covered_fields, &ror_scan->covered_fields); bitmap_union(&info->covered_fields, &ror_scan->covered_fields);
}
if (!info->is_covering && bitmap_is_subset(&info->param->needed_fields, if (!info->is_covering && bitmap_is_subset(&info->param->needed_fields,
&info->covered_fields)) &info->covered_fields))
{ {
DBUG_PRINT("info", ("ROR-intersect is covering now")); DBUG_PRINT("info", ("ROR-intersect is covering now"));
info->is_covering= TRUE; info->is_covering= TRUE;
} }
}
info->total_cost= info->index_scan_costs; info->total_cost= info->index_scan_costs;
DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost));
if (!info->is_covering) if (!info->is_covering)
{ {
ha_rows table_recs= info->param->table->file->records;
info->total_cost += info->total_cost +=
get_sweep_read_cost(info->param, get_sweep_read_cost(info->param, double2rows(info->out_rows));
(ha_rows)(info->records_fract*table_recs)); 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, DBUG_PRINT("info", ("New cost= %g, %scovering", info->total_cost,
info->is_covering?"" : "non-")); 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, ...@@ -2701,9 +2726,13 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info,
a covering ROR-intersection) a covering ROR-intersection)
NOTES NOTES
get_key_scans_params must be called before for the same SEL_TREE before get_key_scans_params must be called before this function can be called.
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: The approximate best non-covering plan search algorithm is as follows:
find_min_ror_intersection_scan() find_min_ror_intersection_scan()
...@@ -2717,11 +2746,11 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, ...@@ -2717,11 +2746,11 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info,
min_scan= make_scan(S); min_scan= make_scan(S);
while (R is not empty) while (R is not empty)
{ {
if (!selectivity(S + first(R) < selectivity(S))) firstR= R - first(R);
if (!selectivity(S + firstR < selectivity(S)))
continue; continue;
S= S + first(R); S= S + first(R);
R= R - first(R);
if (cost(S) < min_cost) if (cost(S) < min_cost)
{ {
min_cost= cost(S); min_cost= cost(S);
...@@ -2752,15 +2781,15 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, ...@@ -2752,15 +2781,15 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
bool *are_all_covering) bool *are_all_covering)
{ {
uint idx; uint idx;
double min_cost= read_time; double min_cost= DBL_MAX;
DBUG_ENTER("get_best_ror_intersect"); DBUG_ENTER("get_best_ror_intersect");
if ((tree->n_ror_scans < 2) || !param->table->file->records) if ((tree->n_ror_scans < 2) || !param->table->file->records)
DBUG_RETURN(NULL); DBUG_RETURN(NULL);
/* /*
Collect ROR-able SEL_ARGs and create ROR_SCAN_INFO for each of them. Step1: Collect ROR-able SEL_ARGs and create ROR_SCAN_INFO for each of
Also find and save clustered PK scan if there is one. them. Also find and save clustered PK scan if there is one.
*/ */
ROR_SCAN_INFO **cur_ror_scan; ROR_SCAN_INFO **cur_ror_scan;
ROR_SCAN_INFO *cpk_scan= NULL; ROR_SCAN_INFO *cpk_scan= NULL;
...@@ -2796,8 +2825,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, ...@@ -2796,8 +2825,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
tree->ror_scans_end);); tree->ror_scans_end););
/* /*
Ok, [ror_scans, ror_scans_end) is array of ptrs to initialized Ok, [ror_scans, ror_scans_end) is array of ptrs to initialized
ROR_SCAN_INFOs. ROR_SCAN_INFO's.
Now, get a minimal key scan using an approximate algorithm. Step 2: Get best ROR-intersection using an approximate algorithm.
*/ */
qsort(tree->ror_scans, tree->n_ror_scans, sizeof(ROR_SCAN_INFO*), qsort(tree->ror_scans, tree->n_ror_scans, sizeof(ROR_SCAN_INFO*),
(qsort_cmp)cmp_ror_scan_info); (qsort_cmp)cmp_ror_scan_info);
...@@ -2814,43 +2843,39 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, ...@@ -2814,43 +2843,39 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
intersect_scans_end= intersect_scans; intersect_scans_end= intersect_scans;
/* Create and incrementally update ROR intersection. */ /* Create and incrementally update ROR intersection. */
ROR_INTERSECT_INFO *intersect; ROR_INTERSECT_INFO *intersect, *intersect_best;
if (!(intersect= ror_intersect_init(param, FALSE))) if (!(intersect= ror_intersect_init(param)) ||
!(intersect_best= ror_intersect_init(param)))
return NULL; 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; 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; cur_ror_scan= tree->ror_scans;
/* Start with one scan */
intersect_scans_best= intersect_scans; intersect_scans_best= intersect_scans;
while (cur_ror_scan != tree->ror_scans_end && !intersect->is_covering) while (cur_ror_scan != tree->ror_scans_end && !intersect->is_covering)
{ {
/* S= S + first(R); */ /* S= S + first(R); R= R - first(R); */
if (ror_intersect_add(param, intersect, *cur_ror_scan)) if (!ror_intersect_add(intersect, *cur_ror_scan, false))
*(intersect_scans_end++)= *cur_ror_scan; {
/* R= R - first(R); */
cur_ror_scan++; cur_ror_scan++;
continue;
}
*(intersect_scans_end++)= *(cur_ror_scan++);
if (intersect->total_cost < min_cost) if (intersect->total_cost < min_cost)
{ {
/* Local minimum found, save it */ /* Local minimum found, save it */
min_cost= intersect->total_cost; ror_intersect_cpy(intersect_best, intersect);
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;
intersect_scans_best= intersect_scans_end; 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, DBUG_EXECUTE("info",print_ror_scans_arr(param->table,
...@@ -2860,48 +2885,26 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, ...@@ -2860,48 +2885,26 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
*are_all_covering= intersect->is_covering; *are_all_covering= intersect->is_covering;
uint best_num= intersect_scans_best - intersect_scans; 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. Ok, found the best ROR-intersection of non-CPK key scans.
Check if we should add a CPK scan. 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 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.
*/ */
if (cpk_scan && !intersect->is_covering) if (cpk_scan && !intersect->is_covering)
{ {
/* if (ror_intersect_add(intersect, cpk_scan, true) &&
Handle the special case: ROR-intersect(PRIMARY, key1) is (intersect->total_cost < min_cost))
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))
{ {
cpk_scan_used= TRUE; cpk_scan_used= TRUE;
min_cost= intersect->total_cost; intersect_best= intersect; //just set pointer here
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;
} }
} }
/* Ok, return ROR-intersect plan if we have found one */ /* Ok, return ROR-intersect plan if we have found one */
TRP_ROR_INTERSECT *trp= NULL; 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)) if (!(trp= new (param->mem_root) TRP_ROR_INTERSECT))
DBUG_RETURN(trp); DBUG_RETURN(trp);
...@@ -2911,13 +2914,17 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, ...@@ -2911,13 +2914,17 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
DBUG_RETURN(NULL); DBUG_RETURN(NULL);
memcpy(trp->first_scan, intersect_scans, best_num*sizeof(ROR_SCAN_INFO*)); memcpy(trp->first_scan, intersect_scans, best_num*sizeof(ROR_SCAN_INFO*));
trp->last_scan= trp->first_scan + best_num; trp->last_scan= trp->first_scan + best_num;
trp->is_covering= is_best_covering; trp->is_covering= intersect_best->is_covering;
trp->read_cost= min_cost; 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->records= best_rows;
trp->index_scan_costs= best_index_scan_costs; trp->index_scan_costs= intersect_best->index_scan_costs;
trp->cpk_scan= cpk_scan; trp->cpk_scan= cpk_scan_used? cpk_scan: NULL;
DBUG_PRINT("info", DBUG_PRINT("info", ("Returning non-covering ROR-intersect plan:"
("Returning non-covering ROR-intersect plan: cost %g, records %lu", "cost %g, records %lu",
trp->read_cost, (ulong) trp->records)); trp->read_cost, (ulong) trp->records));
} }
DBUG_RETURN(trp); DBUG_RETURN(trp);
...@@ -3145,8 +3152,9 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, ...@@ -3145,8 +3152,9 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
found_records) + found_records) +
cpu_cost; cpu_cost;
DBUG_PRINT("info",("read_time: %g found_read_time: %g", DBUG_PRINT("info",("key %s: found_read_time: %g (cur. read_time: %g)",
read_time, found_read_time)); param->table->key_info[keynr].name, found_read_time,
read_time));
if (read_time > found_read_time && found_records != HA_POS_ERROR if (read_time > found_read_time && found_records != HA_POS_ERROR
/*|| read_time == DBL_MAX*/ ) /*|| read_time == DBL_MAX*/ )
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment