Commit e2cd3dd1 authored by timour@mysql.com's avatar timour@mysql.com

WL#1724 "Min/Max Optimization for Queries with Group By Clause"

- after-review changes
- merged with the source tree from 204-08-27
parent a5f063e0
...@@ -200,19 +200,19 @@ select distinct 1 from t1,t3 where t1.a=t3.a; ...@@ -200,19 +200,19 @@ select distinct 1 from t1,t3 where t1.a=t3.a;
1 1
explain SELECT distinct t1.a from t1; explain SELECT distinct t1.a from t1;
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 index NULL PRIMARY 4 NULL 4 Using index 1 SIMPLE t1 range NULL PRIMARY 4 NULL 5 Using index for group-by
explain SELECT distinct t1.a from t1 order by a desc; explain SELECT distinct t1.a from t1 order by a desc;
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 index NULL PRIMARY 4 NULL 4 Using index 1 SIMPLE t1 range NULL PRIMARY 4 NULL 5 Using index for group-by; Using temporary; Using filesort
explain SELECT t1.a from t1 group by a order by a desc; explain SELECT t1.a from t1 group by a order by a desc;
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 index NULL PRIMARY 4 NULL 4 Using index 1 SIMPLE t1 range NULL PRIMARY 4 NULL 5 Using index for group-by; Using temporary; Using filesort
explain SELECT distinct t1.a from t1 order by a desc limit 1; explain SELECT distinct t1.a from t1 order by a desc limit 1;
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 index NULL PRIMARY 4 NULL 4 Using index 1 SIMPLE t1 index NULL PRIMARY 4 NULL 5 Using index
explain SELECT distinct a from t3 order by a desc limit 2; explain SELECT distinct a from t3 order by a desc limit 2;
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 t3 index NULL a 5 NULL 204 Using index 1 SIMPLE t3 index NULL a 5 NULL 10 Using index
explain SELECT distinct a,b from t3 order by a+1; explain SELECT distinct a,b from t3 order by a+1;
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 t3 ALL NULL NULL NULL NULL 204 Using temporary; Using filesort 1 SIMPLE t3 ALL NULL NULL NULL NULL 204 Using temporary; Using filesort
......
This diff is collapsed.
This diff is collapsed.
...@@ -1515,7 +1515,8 @@ longlong ha_myisam::get_auto_increment() ...@@ -1515,7 +1515,8 @@ longlong ha_myisam::get_auto_increment()
int error; int error;
byte key[MI_MAX_KEY_LENGTH]; byte key[MI_MAX_KEY_LENGTH];
(void) extra(HA_EXTRA_KEYREAD); (void) extra(HA_EXTRA_KEYREAD);
key_copy(key,table,table->next_number_index, key_copy(key, table->record[0],
table->key_info + table->next_number_index,
table->next_number_key_offset); table->next_number_key_offset);
error=mi_rkey(file,table->record[1],(int) table->next_number_index, error=mi_rkey(file,table->record[1],(int) table->next_number_index,
key,table->next_number_key_offset,HA_READ_PREFIX_LAST); key,table->next_number_key_offset,HA_READ_PREFIX_LAST);
......
...@@ -984,7 +984,8 @@ longlong handler::get_auto_increment() ...@@ -984,7 +984,8 @@ longlong handler::get_auto_increment()
else else
{ {
byte key[MAX_KEY_LENGTH]; byte key[MAX_KEY_LENGTH];
key_copy(key,table,table->next_number_index, key_copy(key, table->record[0],
table->key_info + table->next_number_index,
table->next_number_key_offset); table->next_number_key_offset);
error=index_read(table->record[1], key, table->next_number_key_offset, error=index_read(table->record[1], key, table->next_number_key_offset,
HA_READ_PREFIX_LAST); HA_READ_PREFIX_LAST);
......
...@@ -159,6 +159,46 @@ bool Item_ident::remove_dependence_processor(byte * arg) ...@@ -159,6 +159,46 @@ bool Item_ident::remove_dependence_processor(byte * arg)
} }
/*
Store the pointer to this item field into a list if not already there.
SYNOPSIS
Item_field::collect_item_field_processor()
arg pointer to a List<Item_field>
DESCRIPTION
The method is used by Item::walk to collect all unique Item_field objects
from a tree of Items into a set of items represented as a list.
IMPLEMENTATION
Item_cond::walk() and Item_func::walk() stop the evaluation of the
processor function for its arguments once the processor returns
true.Therefore in order to force this method being called for all item
arguments in a condition the method must return false.
RETURN
false on success (force the evaluation of collect_item_field_processor
for the subsequent items.)
true o/w (stop evaluation of subsequent items.)
*/
bool Item_field::collect_item_field_processor(byte *arg)
{
DBUG_ENTER("Item_field::collect_item_field_processor");
DBUG_PRINT("info", ("%s", field->field_name ? field->field_name : "noname"));
List<Item_field> *item_list= (List<Item_field>*) arg;
List_iterator<Item_field> item_list_it(*item_list);
Item_field *curr_item;
while ((curr_item= item_list_it++))
{
if (curr_item->eq(this, 1))
DBUG_RETURN(false); /* Already in the set. */
}
item_list->push_back(this);
DBUG_RETURN(false);
}
bool Item::check_cols(uint c) bool Item::check_cols(uint c)
{ {
if (c != 1) if (c != 1)
......
...@@ -260,6 +260,22 @@ class Item { ...@@ -260,6 +260,22 @@ class Item {
virtual bool remove_dependence_processor(byte * arg) { return 0; } virtual bool remove_dependence_processor(byte * arg) { return 0; }
virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; } virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; }
/*
All collect_* methods are used as arguments to walk() to collect
specific types items.
TODO:
A more generic implementation would add a special class
Collect_processor_param that can store arbitrary sets of item kinds
(currently specified as enums), along with a list to store items of the
specified kinds. This would allow to collect combinations of items of
arbitrary kinds without having to add a new collect method each time.
There can be one generic collect_processor method that checks the item type
and compares it with the item types in Collect_processor_param.
*/
virtual bool collect_item_field_processor(byte * arg) { return 0; }
virtual bool collect_item_sum_min_processor(byte * arg) { return 0; }
virtual bool collect_item_sum_max_processor(byte * arg) { return 0; }
virtual bool has_non_min_max_sum_processor(byte * arg) { return 0; }
virtual Item *this_item() { return this; } /* For SPs mostly. */ virtual Item *this_item() { return this; } /* For SPs mostly. */
virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */ virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */
...@@ -493,6 +509,7 @@ class Item_field :public Item_ident ...@@ -493,6 +509,7 @@ class Item_field :public Item_ident
bool get_time(TIME *ltime); bool get_time(TIME *ltime);
bool is_null() { return field->is_null(); } bool is_null() { return field->is_null(); }
Item *get_tmp_table_item(THD *thd); Item *get_tmp_table_item(THD *thd);
bool collect_item_field_processor(byte * arg);
void cleanup(); void cleanup();
inline uint32 max_disp_length() { return field->max_length(); } inline uint32 max_disp_length() { return field->max_length(); }
friend class Item_default_value; friend class Item_default_value;
...@@ -984,6 +1001,8 @@ class Item_ref :public Item_ident ...@@ -984,6 +1001,8 @@ class Item_ref :public Item_ident
(*ref)->save_in_field(result_field, no_conversions); (*ref)->save_in_field(result_field, no_conversions);
} }
Item *real_item() { return *ref; } Item *real_item() { return *ref; }
bool walk(Item_processor processor, byte *arg)
{ return (*ref)->walk(processor, arg); }
void print(String *str); void print(String *str);
void cleanup(); void cleanup();
}; };
......
...@@ -182,6 +182,45 @@ bool Item_sum::walk (Item_processor processor, byte *argument) ...@@ -182,6 +182,45 @@ bool Item_sum::walk (Item_processor processor, byte *argument)
return (this->*processor)(argument); return (this->*processor)(argument);
} }
/*
Store the pointer to this item into a list if not already there.
SYNOPSIS
Item_sum::collect()
item_list pointer to a List<Item_sum> where item_sum objects are collected
DESCRIPTION
The method is used by collect_item_sum_*_processor, called by
Item_sum::walk, to collect all unique Item_sum_min and Item_sum_max objects
from a tree of Items into a set of items represented as a list.
IMPLEMENTATION
Item_cond::walk() and Item_func::walk() stop the evaluation of the
processor function for its arguments once the processor returns
true.Therefore in order to force this method being called for all item
arguments in a condition the method must return false.
RETURN
FALSE on success (force the evaluation of collect_item_sum_*_processor
for the subsequent items.)
TRUE o/w (stop evaluation of subsequent items.)
*/
bool Item_sum::collect(List<Item_sum> *item_list)
{
List_iterator<Item_sum> item_list_it(*item_list);
Item_sum *curr_item;
while ((curr_item= item_list_it++))
{
if (curr_item == this)
return FALSE; /* Already in the set. */
}
item_list->push_back(this);
return FALSE;
}
String * String *
Item_sum_num::val_str(String *str) Item_sum_num::val_str(String *str)
{ {
......
...@@ -27,6 +27,8 @@ class Item_arena; ...@@ -27,6 +27,8 @@ class Item_arena;
class Item_sum :public Item_result_field class Item_sum :public Item_result_field
{ {
private:
bool collect(List<Item_sum> *item_list);
public: public:
enum Sumfunctype enum Sumfunctype
{ COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC, { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC,
...@@ -98,6 +100,33 @@ class Item_sum :public Item_result_field ...@@ -98,6 +100,33 @@ class Item_sum :public Item_result_field
bool save_args(Item_arena* stmt); bool save_args(Item_arena* stmt);
bool walk (Item_processor processor, byte *argument); bool walk (Item_processor processor, byte *argument);
/* Collect Item_sum_min objects into a list supplied by the caller. */
bool collect_item_sum_min_processor(byte *arg)
{
if (Item_sum::MIN_FUNC == this->sum_func())
return collect((List<Item_sum>*) arg);
else
return FALSE;
}
/* Collect Item_sum_max objects into a list supplied by the caller. */
bool collect_item_sum_max_processor(byte *arg)
{
if (Item_sum::MAX_FUNC == this->sum_func())
return collect((List<Item_sum>*) arg);
else
return FALSE;
}
/* Check if there are any aggregate functions other than MIN and MAX. */
bool has_non_min_max_sum_processor(byte * arg)
{
Sumfunctype sum_type= this->sum_func();
if ((sum_type != Item_sum::MIN_FUNC) && (sum_type != Item_sum::MAX_FUNC))
return TRUE;
return FALSE;
}
}; };
......
...@@ -66,94 +66,121 @@ int find_ref_key(TABLE *table,Field *field, uint *key_length) ...@@ -66,94 +66,121 @@ int find_ref_key(TABLE *table,Field *field, uint *key_length)
} }
/* Copy a key from record to some buffer */ /*
/* if length == 0 then copy whole key */ Copy part of a record that forms a key or key prefix to a buffer.
SYNOPSIS
key_copy()
to_key buffer that will be used as a key
from_record full record to be copied from
key_info descriptor of the index
key_length specifies length of all keyparts that will be copied
DESCRIPTION
The function takes a complete table record (as e.g. retrieved by
handler::index_read()), and a description of an index on the same table,
and extracts the first key_length bytes of the record which are part of a
key into to_key. If length == 0 then copy all bytes from the record that
form a key.
RETURN
None
*/
void key_copy(byte *key,TABLE *table,uint idx,uint key_length) void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length)
{ {
uint length; uint length;
KEY *key_info=table->key_info+idx;
KEY_PART_INFO *key_part; KEY_PART_INFO *key_part;
if (key_length == 0) if (key_length == 0)
key_length=key_info->key_length; key_length= key_info->key_length;
for (key_part=key_info->key_part; for (key_part= key_info->key_part; (int) key_length > 0; key_part++)
(int) key_length > 0 ;
key_part++)
{ {
if (key_part->null_bit) if (key_part->null_bit)
{ {
*key++= test(table->record[0][key_part->null_offset] & *to_key++= test(from_record[key_part->null_offset] &
key_part->null_bit); key_part->null_bit);
key_length--; key_length--;
} }
if (key_part->key_part_flag & HA_BLOB_PART) if (key_part->key_part_flag & HA_BLOB_PART)
{ {
char *pos; char *pos;
ulong blob_length=((Field_blob*) key_part->field)->get_length(); ulong blob_length= ((Field_blob*) key_part->field)->get_length();
key_length-=2; key_length-= 2;
((Field_blob*) key_part->field)->get_ptr(&pos); ((Field_blob*) key_part->field)->get_ptr(&pos);
length=min(key_length,key_part->length); length=min(key_length, key_part->length);
set_if_smaller(blob_length,length); set_if_smaller(blob_length, length);
int2store(key,(uint) blob_length); int2store(to_key, (uint) blob_length);
key+=2; // Skip length info to_key+= 2; // Skip length info
memcpy(key,pos,blob_length); memcpy(to_key, pos, blob_length);
} }
else else
{ {
length=min(key_length,key_part->length); length= min(key_length, key_part->length);
memcpy(key,table->record[0]+key_part->offset,(size_t) length); memcpy(to_key, from_record + key_part->offset, (size_t) length);
} }
key+=length; to_key+= length;
key_length-=length; key_length-= length;
} }
} /* key_copy */ }
/* restore a key from some buffer to record */ /*
Restore a key from some buffer to record.
SYNOPSIS
key_restore()
to_record record buffer where the key will be restored to
from_key buffer that contains a key
key_info descriptor of the index
key_length specifies length of all keyparts that will be restored
DESCRIPTION
This function converts a key into record format. It can be used in cases
when we want to return a key as a result row.
RETURN
None
*/
void key_restore(TABLE *table,byte *key,uint idx,uint key_length) void key_restore(byte *to_record, byte *from_key, KEY *key_info,
uint key_length)
{ {
uint length; uint length;
KEY *key_info=table->key_info+idx;
KEY_PART_INFO *key_part; KEY_PART_INFO *key_part;
if (key_length == 0) if (key_length == 0)
{ {
if (idx == (uint) -1) key_length= key_info->key_length;
return;
key_length=key_info->key_length;
} }
for (key_part=key_info->key_part; for (key_part= key_info->key_part ; (int) key_length > 0 ; key_part++)
(int) key_length > 0 ;
key_part++)
{ {
if (key_part->null_bit) if (key_part->null_bit)
{ {
if (*key++) if (*from_key++)
table->record[0][key_part->null_offset]|= key_part->null_bit; to_record[key_part->null_offset]|= key_part->null_bit;
else else
table->record[0][key_part->null_offset]&= ~key_part->null_bit; to_record[key_part->null_offset]&= ~key_part->null_bit;
key_length--; key_length--;
} }
if (key_part->key_part_flag & HA_BLOB_PART) if (key_part->key_part_flag & HA_BLOB_PART)
{ {
uint blob_length=uint2korr(key); uint blob_length= uint2korr(from_key);
key+=2; from_key+= 2;
key_length-=2; key_length-= 2;
((Field_blob*) key_part->field)->set_ptr((ulong) blob_length, ((Field_blob*) key_part->field)->set_ptr((ulong) blob_length,
(char*) key); (char*) from_key);
length=key_part->length; length= key_part->length;
} }
else else
{ {
length=min(key_length,key_part->length); length= min(key_length, key_part->length);
memcpy(table->record[0]+key_part->offset,key,(size_t) length); memcpy(to_record + key_part->offset, from_key, (size_t) length);
} }
key+=length; from_key+= length;
key_length-=length; key_length-= length;
} }
} /* key_restore */ }
/* /*
......
...@@ -733,7 +733,8 @@ bool add_proc_to_list(THD *thd, Item *item); ...@@ -733,7 +733,8 @@ bool add_proc_to_list(THD *thd, Item *item);
TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find); TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
SQL_SELECT *make_select(TABLE *head, table_map const_tables, SQL_SELECT *make_select(TABLE *head, table_map const_tables,
table_map read_tables, COND *conds, int *error); table_map read_tables, COND *conds, int *error,
bool allow_null_cond= false);
enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND, enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
IGNORE_ERRORS}; IGNORE_ERRORS};
extern const Item **not_found_item; extern const Item **not_found_item;
...@@ -814,8 +815,9 @@ void print_plan(JOIN* join, double read_time, double record_count, ...@@ -814,8 +815,9 @@ void print_plan(JOIN* join, double read_time, double record_count,
void mysql_print_status(THD *thd); void mysql_print_status(THD *thd);
/* key.cc */ /* key.cc */
int find_ref_key(TABLE *form,Field *field, uint *offset); int find_ref_key(TABLE *form,Field *field, uint *offset);
void key_copy(byte *key,TABLE *form,uint index,uint key_length); void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length);
void key_restore(TABLE *form,byte *key,uint index,uint key_length); void key_restore(byte *to_record, byte *from_key, KEY *key_info,
uint key_length);
bool key_cmp_if_same(TABLE *form,const byte *key,uint index,uint key_length); bool key_cmp_if_same(TABLE *form,const byte *key,uint index,uint key_length);
void key_unpack(String *to,TABLE *form,uint index); void key_unpack(String *to,TABLE *form,uint index);
bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields); bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields);
......
This diff is collapsed.
...@@ -146,7 +146,8 @@ class QUICK_SELECT_I ...@@ -146,7 +146,8 @@ class QUICK_SELECT_I
QS_TYPE_RANGE_DESC = 2, QS_TYPE_RANGE_DESC = 2,
QS_TYPE_FULLTEXT = 3, QS_TYPE_FULLTEXT = 3,
QS_TYPE_ROR_INTERSECT = 4, QS_TYPE_ROR_INTERSECT = 4,
QS_TYPE_ROR_UNION = 5 QS_TYPE_ROR_UNION = 5,
QS_TYPE_GROUP_MIN_MAX = 6
}; };
/* Get type of this quick select - one of the QS_TYPE_* values */ /* Get type of this quick select - one of the QS_TYPE_* values */
...@@ -278,14 +279,12 @@ class QUICK_RANGE_SELECT : public QUICK_SELECT_I ...@@ -278,14 +279,12 @@ class QUICK_RANGE_SELECT : public QUICK_SELECT_I
int init(); int init();
int get_next(); int get_next();
void range_end(); void range_end();
int get_next_prefix(uint prefix_length, byte *cur_prefix);
bool reverse_sorted() { return 0; } bool reverse_sorted() { return 0; }
bool unique_key_range(); bool unique_key_range();
int init_ror_merged_scan(bool reuse_handler); int init_ror_merged_scan(bool reuse_handler);
void save_last_pos() void save_last_pos()
{ { file->position(record); }
file->position(record);
};
int get_type() { return QS_TYPE_RANGE; } int get_type() { return QS_TYPE_RANGE; }
void add_keys_and_lengths(String *key_names, String *used_lengths); void add_keys_and_lengths(String *key_names, String *used_lengths);
void add_info_string(String *str); void add_info_string(String *str);
...@@ -518,6 +517,103 @@ class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I ...@@ -518,6 +517,103 @@ class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I
}; };
/*
Index scan for GROUP-BY queries with MIN/MAX aggregate functions.
This class provides a specialized index access method for GROUP-BY queries
of the forms:
SELECT A_1,...,A_k, [B_1,...,B_m], [MIN(C)], [MAX(C)]
FROM T
WHERE [RNG(A_1,...,A_p ; where p <= k)]
[AND EQ(B_1,...,B_m)]
[AND PC(C)]
[AND PA(A_i1,...,A_iq)]
GROUP BY A_1,...,A_k;
or
SELECT DISTINCT A_i1,...,A_ik
FROM T
WHERE [RNG(A_1,...,A_p ; where p <= k)]
[AND PA(A_i1,...,A_iq)];
where all selected fields are parts of the same index.
The class of queries that can be processed by this quick select is fully
specified in the description of get_best_trp_group_min_max() in opt_range.cc.
The get_next() method directly produces result tuples, thus obviating the
need to call end_send_group() because all grouping is already done inside
get_next().
Since one of the requirements is that all select fields are part of the same
index, this class produces only index keys, and not complete records.
*/
class QUICK_GROUP_MIN_MAX_SELECT : public QUICK_SELECT_I
{
private:
handler *file; /* The handler used to get data. */
JOIN *join; /* Descriptor of the current query */
KEY *index_info; /* The index chosen for data access */
byte *record; /* Buffer where the next record is returned. */
byte *tmp_record; /* Temporary storage for next_min(), next_max(). */
byte *group_prefix; /* Key prefix consisting of the GROUP fields. */
uint group_prefix_len; /* Length of the group prefix. */
byte *last_prefix; /* Prefix of the last group for detecting EOF. */
bool have_min; /* Specify whether we are computing */
bool have_max; /* a MIN, a MAX, or both. */
bool seen_first_key; /* Denotes whether the first key was retrieved.*/
KEY_PART_INFO *min_max_arg_part; /* The keypart of the only argument field */
/* of all MIN/MAX functions. */
uint min_max_arg_len; /* The length of the MIN/MAX argument field */
byte *key_infix; /* Infix of constants from equality predicates. */
uint key_infix_len;
DYNAMIC_ARRAY min_max_ranges; /* Array of range ptrs for the MIN/MAX field. */
uint real_prefix_len; /* Length of key prefix extended with key_infix. */
List<Item_sum> *min_functions;
List<Item_sum> *max_functions;
List_iterator<Item_sum> *min_functions_it;
List_iterator<Item_sum> *max_functions_it;
public:
/*
The following two members are public to allow easy access from
TRP_GROUP_MIN_MAX::make_quick()
*/
MEM_ROOT alloc; /* Memory pool for this and quick_prefix_select data. */
QUICK_RANGE_SELECT *quick_prefix_select;/* For retrieval of group prefixes. */
private:
int next_prefix();
int next_min_in_range();
int next_max_in_range();
int next_min();
int next_max();
void update_min_result();
void update_max_result();
public:
QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join, bool have_min,
bool have_max, KEY_PART_INFO *min_max_arg_part,
uint group_prefix_len, uint used_key_parts,
KEY *index_info, uint use_index, double read_cost,
ha_rows records, uint key_infix_len,
byte *key_infix, MEM_ROOT *parent_alloc);
~QUICK_GROUP_MIN_MAX_SELECT();
bool add_range(SEL_ARG *sel_range);
void update_key_stat();
bool alloc_buffers();
int init();
int reset();
int get_next();
bool reverse_sorted() { return false; }
bool unique_key_range() { return false; }
int get_type() { return QS_TYPE_GROUP_MIN_MAX; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
};
class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
{ {
public: public:
......
...@@ -336,7 +336,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) ...@@ -336,7 +336,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
1 Otherwise 1 Otherwise
*/ */
static bool simple_pred(Item_func *func_item, Item **args, bool *inv_order) bool simple_pred(Item_func *func_item, Item **args, bool *inv_order)
{ {
Item *item; Item *item;
*inv_order= 0; *inv_order= 0;
......
...@@ -1884,7 +1884,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) ...@@ -1884,7 +1884,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
col_privs->field[1]->pack_length()+ col_privs->field[1]->pack_length()+
col_privs->field[2]->pack_length()+ col_privs->field[2]->pack_length()+
col_privs->field[3]->pack_length()); col_privs->field[3]->pack_length());
key_copy(key,col_privs,0,key_len); key_copy(key,col_privs->record[0],col_privs->key_info,key_len);
col_privs->field[4]->store("",0, &my_charset_latin1); col_privs->field[4]->store("",0, &my_charset_latin1);
col_privs->file->ha_index_init(0); col_privs->file->ha_index_init(0);
if (col_privs->file->index_read(col_privs->record[0], if (col_privs->file->index_read(col_privs->record[0],
...@@ -1996,7 +1996,7 @@ static int replace_column_table(GRANT_TABLE *g_t, ...@@ -1996,7 +1996,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
table->field[3]->store(table_name,(uint) strlen(table_name), &my_charset_latin1); table->field[3]->store(table_name,(uint) strlen(table_name), &my_charset_latin1);
key_length=(table->field[0]->pack_length()+ table->field[1]->pack_length()+ key_length=(table->field[0]->pack_length()+ table->field[1]->pack_length()+
table->field[2]->pack_length()+ table->field[3]->pack_length()); table->field[2]->pack_length()+ table->field[3]->pack_length());
key_copy(key,table,0,key_length); key_copy(key,table->record[0],table->key_info,key_length);
rights &= COL_ACLS; // Only ACL for columns rights &= COL_ACLS; // Only ACL for columns
...@@ -2009,7 +2009,7 @@ static int replace_column_table(GRANT_TABLE *g_t, ...@@ -2009,7 +2009,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
{ {
ulong privileges = xx->rights; ulong privileges = xx->rights;
bool old_row_exists=0; bool old_row_exists=0;
key_restore(table,key,0,key_length); key_restore(table->record[0],key,table->key_info,key_length);
table->field[4]->store(xx->column.ptr(),xx->column.length(), table->field[4]->store(xx->column.ptr(),xx->column.length(),
&my_charset_latin1); &my_charset_latin1);
...@@ -2025,7 +2025,7 @@ static int replace_column_table(GRANT_TABLE *g_t, ...@@ -2025,7 +2025,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
} }
old_row_exists = 0; old_row_exists = 0;
restore_record(table,default_values); // Get empty record restore_record(table,default_values); // Get empty record
key_restore(table,key,0,key_length); key_restore(table->record[0],key,table->key_info,key_length);
table->field[4]->store(xx->column.ptr(),xx->column.length(), table->field[4]->store(xx->column.ptr(),xx->column.length(),
&my_charset_latin1); &my_charset_latin1);
} }
......
...@@ -333,7 +333,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -333,7 +333,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
send_error(thd,ER_OUTOFMEMORY); send_error(thd,ER_OUTOFMEMORY);
goto err; goto err;
} }
key_copy(key, table, keyno, key_len); key_copy(key, table->record[0], table->key_info + keyno, key_len);
err=table->file->index_read(table->record[0], err=table->file->index_read(table->record[0],
key,key_len,ha_rkey_mode); key,key_len,ha_rkey_mode);
mode=rkey_to_rnext[(int)ha_rkey_mode]; mode=rkey_to_rnext[(int)ha_rkey_mode];
......
...@@ -635,7 +635,7 @@ int write_record(TABLE *table,COPY_INFO *info) ...@@ -635,7 +635,7 @@ int write_record(TABLE *table,COPY_INFO *info)
goto err; goto err;
} }
} }
key_copy((byte*) key,table,key_nr,0); key_copy((byte*) key,table->record[0],table->key_info+key_nr,0);
if ((error=(table->file->index_read_idx(table->record[1],key_nr, if ((error=(table->file->index_read_idx(table->record[1],key_nr,
(byte*) key, (byte*) key,
table->key_info[key_nr]. table->key_info[key_nr].
......
...@@ -188,6 +188,7 @@ static bool update_sum_func(Item_sum **func); ...@@ -188,6 +188,7 @@ static bool update_sum_func(Item_sum **func);
static void select_describe(JOIN *join, bool need_tmp_table,bool need_order, static void select_describe(JOIN *join, bool need_tmp_table,bool need_order,
bool distinct, const char *message=NullS); bool distinct, const char *message=NullS);
static Item *remove_additional_cond(Item* conds); static Item *remove_additional_cond(Item* conds);
static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab);
/* /*
...@@ -635,7 +636,7 @@ JOIN::optimize() ...@@ -635,7 +636,7 @@ JOIN::optimize()
conds=new Item_int((longlong) 1,1); // Always true conds=new Item_int((longlong) 1,1); // Always true
} }
select=make_select(*table, const_table_map, select=make_select(*table, const_table_map,
const_table_map, conds, &error); const_table_map, conds, &error, true);
if (error) if (error)
{ /* purecov: inspected */ { /* purecov: inspected */
error= -1; /* purecov: inspected */ error= -1; /* purecov: inspected */
...@@ -2304,6 +2305,12 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, ...@@ -2304,6 +2305,12 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
if (s->worst_seeks < 2.0) // Fix for small tables if (s->worst_seeks < 2.0) // Fix for small tables
s->worst_seeks=2.0; s->worst_seeks=2.0;
/*
Add to stat->const_keys those indexes for which all group fields or
all select distinct fields participate in one index.
*/
add_group_and_distinct_keys(join, s);
if (!s->const_keys.is_clear_all() && if (!s->const_keys.is_clear_all() &&
!s->table->pos_in_table_list->embedding) !s->table->pos_in_table_list->embedding)
{ {
...@@ -2312,7 +2319,9 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, ...@@ -2312,7 +2319,9 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
select= make_select(s->table, found_const_table_map, select= make_select(s->table, found_const_table_map,
found_const_table_map, found_const_table_map,
s->on_expr ? s->on_expr : conds, s->on_expr ? s->on_expr : conds,
&error); &error, true);
if (!select)
DBUG_RETURN(1);
records= get_quick_record_count(join->thd, select, s->table, records= get_quick_record_count(join->thd, select, s->table,
&s->const_keys, join->row_limit); &s->const_keys, join->row_limit);
s->quick=select->quick; s->quick=select->quick;
...@@ -2347,13 +2356,13 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, ...@@ -2347,13 +2356,13 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
} }
} }
/* Find best combination and return it */
join->join_tab=stat; join->join_tab=stat;
join->map2table=stat_ref; join->map2table=stat_ref;
join->table= join->all_tables=table_vector; join->table= join->all_tables=table_vector;
join->const_tables=const_count; join->const_tables=const_count;
join->found_const_table_map=found_const_table_map; join->found_const_table_map=found_const_table_map;
/* Find an optimal join order of the non-constant tables. */
if (join->const_tables != join->tables) if (join->const_tables != join->tables)
{ {
optimize_keyuse(join, keyuse_array); optimize_keyuse(join, keyuse_array);
...@@ -2365,6 +2374,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, ...@@ -2365,6 +2374,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
sizeof(POSITION)*join->const_tables); sizeof(POSITION)*join->const_tables);
join->best_read=1.0; join->best_read=1.0;
} }
/* Generate an execution plan from the found optimal join order. */
DBUG_RETURN(join->thd->killed || get_best_combination(join)); DBUG_RETURN(join->thd->killed || get_best_combination(join));
} }
...@@ -2561,6 +2571,10 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond, ...@@ -2561,6 +2571,10 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond,
bool is_const=1; bool is_const=1;
for (uint i=0; i<num_values; i++) for (uint i=0; i<num_values; i++)
/*
TODO: this looks like a bug, should be
is_const&= (value[i])->const_item();
*/
is_const&= (*value)->const_item(); is_const&= (*value)->const_item();
if (is_const) if (is_const)
stat[0].const_keys.merge(possible_keys); stat[0].const_keys.merge(possible_keys);
...@@ -2997,6 +3011,68 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) ...@@ -2997,6 +3011,68 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
} }
/*
Discover the indexes that can be used for GROUP BY or DISTINCT queries.
SYNOPSIS
add_group_and_distinct_keys()
join
join_tab
DESCRIPTION
If the query has a GROUP BY clause, find all indexes that contain all
GROUP BY fields, and add those indexes to join->const_keys.
If the query has a DISTINCT clause, find all indexes that contain all
SELECT fields, and add those indexes to join->const_keys.
This allows later on such queries to be processed by a
QUICK_GROUP_MIN_MAX_SELECT.
RETURN
None
*/
static void
add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab)
{
List<Item_field> indexed_fields;
List_iterator<Item_field> indexed_fields_it(indexed_fields);
ORDER *cur_group;
Item_field *cur_item;
key_map possible_keys(0);
if (join->group_list)
{ /* Collect all query fields referenced in the GROUP clause. */
for (cur_group= join->group_list; cur_group; cur_group= cur_group->next)
(*cur_group->item)->walk(&Item::collect_item_field_processor,
(byte*) &indexed_fields);
}
else if (join->select_distinct)
{ /* Collect all query fields referenced in the SELECT clause. */
List<Item> &select_items= join->fields_list;
List_iterator<Item> select_items_it(select_items);
Item *item;
while ((item= select_items_it++))
item->walk(&Item::collect_item_field_processor, (byte*) &indexed_fields);
}
else
return;
if (indexed_fields.elements == 0)
return;
/* Intersect the keys of all group fields. */
cur_item= indexed_fields_it++;
possible_keys.merge(cur_item->field->part_of_key);
while ((cur_item= indexed_fields_it++))
{
possible_keys.intersect(cur_item->field->part_of_key);
}
if (!possible_keys.is_clear_all())
join_tab->const_keys.merge(possible_keys);
}
/***************************************************************************** /*****************************************************************************
Go through all combinations of not marked tables and find the one Go through all combinations of not marked tables and find the one
which uses least records which uses least records
...@@ -4883,6 +4959,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -4883,6 +4959,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (select) if (select)
{ {
table_map used_tables; table_map used_tables;
if (cond) /* Because of QUICK_GROUP_MIN_MAX_SELECT */
{ /* there may be a select without a cond. */
if (join->tables > 1) if (join->tables > 1)
cond->update_used_tables(); // Tablenr may have changed cond->update_used_tables(); // Tablenr may have changed
if (join->const_tables == join->tables && if (join->const_tables == join->tables &&
...@@ -4899,6 +4977,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -4899,6 +4977,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
DBUG_RETURN(1); // Impossible const condition DBUG_RETURN(1); // Impossible const condition
} }
} }
}
used_tables=((select->const_tables=join->const_table_map) | used_tables=((select->const_tables=join->const_table_map) |
OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); OUTER_REF_TABLE_BIT | RAND_TABLE_BIT);
for (uint i=join->const_tables ; i < join->tables ; i++) for (uint i=join->const_tables ; i < join->tables ; i++)
...@@ -4928,8 +5007,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -4928,8 +5007,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
join->best_positions[i].records_read= rows2double(tab->quick->records); join->best_positions[i].records_read= rows2double(tab->quick->records);
} }
COND *tmp=make_cond_for_table(cond,used_tables,current_map); COND *tmp= NULL;
if (!tmp && tab->quick) if (cond)
tmp= make_cond_for_table(cond,used_tables,current_map);
if (cond && !tmp && tab->quick)
{ // Outer join { // Outer join
/* /*
Hack to handle the case where we only refer to a table Hack to handle the case where we only refer to a table
...@@ -4937,8 +5018,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -4937,8 +5018,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
*/ */
tmp=new Item_int((longlong) 1,1); // Always true tmp=new Item_int((longlong) 1,1); // Always true
} }
if (tmp) if (tmp || !cond)
{ {
if (tmp)
DBUG_EXECUTE("where",print_where(tmp,tab->table->table_name);); DBUG_EXECUTE("where",print_where(tmp,tab->table->table_name););
SQL_SELECT *sel=tab->select=(SQL_SELECT*) SQL_SELECT *sel=tab->select=(SQL_SELECT*)
join->thd->memdup((gptr) select, sizeof(SQL_SELECT)); join->thd->memdup((gptr) select, sizeof(SQL_SELECT));
...@@ -4950,9 +5032,17 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -4950,9 +5032,17 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
The guard will turn the predicate on only after The guard will turn the predicate on only after
the first match for outer tables is encountered. the first match for outer tables is encountered.
*/ */
if (cond)
{/*
Because of QUICK_GROUP_MIN_MAX_SELECT there may be a select without
a cond, so neutralize the hack above.
*/
if (!(tmp= add_found_match_trig_cond(first_inner_tab, tmp, 0))) if (!(tmp= add_found_match_trig_cond(first_inner_tab, tmp, 0)))
DBUG_RETURN(1); DBUG_RETURN(1);
tab->select_cond=sel->cond=tmp; tab->select_cond=sel->cond=tmp;
}
else
tab->select_cond= sel->cond= NULL;
sel->head=tab->table; sel->head=tab->table;
if (tab->quick) if (tab->quick)
...@@ -4992,7 +5082,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -4992,7 +5082,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
the index if we are using limit and this is the first table the index if we are using limit and this is the first table
*/ */
if ((!tab->keys.is_subset(tab->const_keys) && i > 0) || if (cond &&
(!tab->keys.is_subset(tab->const_keys) && i > 0) ||
(!tab->const_keys.is_clear_all() && i == join->const_tables && (!tab->const_keys.is_clear_all() && i == join->const_tables &&
join->unit->select_limit_cnt < join->unit->select_limit_cnt <
join->best_positions[i].records_read && join->best_positions[i].records_read &&
...@@ -5050,7 +5141,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -5050,7 +5141,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
} }
if (i != join->const_tables && tab->use_quick != 2) if (i != join->const_tables && tab->use_quick != 2)
{ /* Read with cache */ { /* Read with cache */
if ((tmp=make_cond_for_table(cond, if (cond &&
(tmp=make_cond_for_table(cond,
join->const_table_map | join->const_table_map |
current_map, current_map,
current_map))) current_map)))
...@@ -7444,11 +7536,18 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) ...@@ -7444,11 +7536,18 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
} }
else else
{ {
if (join->sort_and_group || (join->procedure && /* Test if data is accessed via QUICK_GROUP_MIN_MAX_SELECT. */
join->procedure->flags & PROC_GROUP)) bool is_using_quick_group_min_max_select=
end_select=end_send_group; (join->join_tab->select && join->join_tab->select->quick &&
(join->join_tab->select->quick->get_type() ==
QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX));
if ((join->sort_and_group ||
(join->procedure && join->procedure->flags & PROC_GROUP)) &&
!is_using_quick_group_min_max_select)
end_select= end_send_group;
else else
end_select=end_send; end_select= end_send;
} }
join->join_tab[join->tables-1].next_select=end_select; join->join_tab[join->tables-1].next_select=end_select;
...@@ -9225,7 +9324,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, ...@@ -9225,7 +9324,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
& HA_READ_PREV) || & HA_READ_PREV) ||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION) quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
DBUG_RETURN(0); // Use filesort DBUG_RETURN(0); // Use filesort
/* ORDER BY range_key DESC */ /* ORDER BY range_key DESC */
...@@ -11455,11 +11555,16 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -11455,11 +11555,16 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
extra.append(tab->keys.print(buf)); extra.append(tab->keys.print(buf));
extra.append(')'); extra.append(')');
} }
else else if (tab->select->cond)
extra.append("; Using where"); extra.append("; Using where");
} }
if (key_read) if (key_read)
{
if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
extra.append("; Using index for group-by");
else
extra.append("; Using index"); extra.append("; Using index");
}
if (table->reginfo.not_exists_optimize) if (table->reginfo.not_exists_optimize)
extra.append("; Not exists"); extra.append("; Not exists");
if (need_tmp_table) if (need_tmp_table)
......
...@@ -399,6 +399,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, ...@@ -399,6 +399,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
uint find_shortest_key(TABLE *table, const key_map *usable_keys); uint find_shortest_key(TABLE *table, const key_map *usable_keys);
/* functions from opt_sum.cc */ /* functions from opt_sum.cc */
bool simple_pred(Item_func *func_item, Item **args, bool *inv_order);
int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds); int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds);
/* from sql_delete.cc, used by opt_range.cc */ /* from sql_delete.cc, used by opt_range.cc */
......
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