Commit d89166cd authored by sergefp@mysql.com's avatar sergefp@mysql.com

Merge spetrunia@bk-internal.mysql.com:/home/bk/mysql-5.0

into mysql.com:/home/psergey/mysql-5.0-fix-vg1
parents 4aebc4b3 1eab462c
......@@ -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
......
drop table if exists t1;
drop table if exists t1,t2;
create table t1
(
key1 int not null,
......
......@@ -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
......
......@@ -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
......
......@@ -50,6 +50,11 @@
#define test_use_count(A) {}
#endif
/*
Convert double value to #rows. Currently this does floor(), and we
might consider using round() instead.
*/
#define double2rows(x) ((ha_rows)(x))
static int sel_cmp(Field *f,char *a,char *b,uint8 a_flag,uint8 b_flag);
......@@ -1628,6 +1633,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 +1880,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 +1919,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 +2400,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 +2428,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 +2441,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.0;
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 +2547,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 +2580,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 +2591,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,49 +2611,105 @@ 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);
......@@ -2701,9 +2731,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 +2751,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 +2786,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 +2830,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 +2848,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 +2890,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 +2919,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 +3157,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*/ )
......
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