summaryrefslogtreecommitdiff
path: root/sql/sql_select.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_select.cc')
-rw-r--r--sql/sql_select.cc128
1 files changed, 105 insertions, 23 deletions
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 7119650a7a6..83da76bc7dc 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -291,6 +291,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
all_fields List of all fields used in select
select Current select
ref_pointer_array Array of references to Items used in current select
+ group_list GROUP BY list (is NULL by default)
DESCRIPTION
The function serves 3 purposes - adds fields referenced from inner
@@ -309,6 +310,8 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
function is aggregated in the select where the outer field was
resolved or in some more inner select then the Item_direct_ref
class should be used.
+ Also it should be used if we are grouping by a subquery containing
+ the outer field.
The resolution is done here and not at the fix_fields() stage as
it can be done only after sum functions are fixed and pulled up to
selects where they are have to be aggregated.
@@ -325,7 +328,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
bool
fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
- Item **ref_pointer_array)
+ Item **ref_pointer_array, ORDER *group_list)
{
Item_outer_ref *ref;
bool res= FALSE;
@@ -375,6 +378,22 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
}
}
}
+ else
+ {
+ /*
+ Check if GROUP BY item trees contain the outer ref:
+ in this case we have to use Item_direct_ref instead of Item_ref.
+ */
+ for (ORDER *group= group_list; group; group= group->next)
+ {
+ if ((*group->item)->walk(&Item::find_item_processor, TRUE,
+ (uchar *) ref))
+ {
+ direct_ref= TRUE;
+ break;
+ }
+ }
+ }
new_ref= direct_ref ?
new Item_direct_ref(ref->context, item_ref, ref->table_name,
ref->field_name, ref->alias_name_used) :
@@ -581,7 +600,8 @@ JOIN::prepare(Item ***rref_pointer_array,
}
if (select_lex->inner_refs_list.elements &&
- fix_inner_refs(thd, all_fields, select_lex, ref_pointer_array))
+ fix_inner_refs(thd, all_fields, select_lex, ref_pointer_array,
+ group_list))
DBUG_RETURN(-1);
if (group_list)
@@ -2337,7 +2357,13 @@ JOIN::destroy()
tab->cleanup();
}
tmp_join->tmp_join= 0;
- tmp_table_param.copy_field= 0;
+ /*
+ We need to clean up tmp_table_param for reusable JOINs (having non-zero
+ and different from self tmp_join) because it's not being cleaned up
+ anywhere else (as we need to keep the join is reusable).
+ */
+ tmp_table_param.cleanup();
+ tmp_table_param.copy_field= tmp_join->tmp_table_param.copy_field= 0;
DBUG_RETURN(tmp_join->destroy());
}
cond_equal= 0;
@@ -5946,6 +5972,12 @@ JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table)
const_table_map= 0;
tmp_table_param.field_count= tmp_table_param.sum_func_count=
tmp_table_param.func_count= 0;
+ /*
+ We need to destruct the copy_field (allocated in create_tmp_table())
+ before setting it to 0 if the join is not "reusable".
+ */
+ if (!tmp_join || tmp_join != this)
+ tmp_table_param.cleanup();
tmp_table_param.copy_field= tmp_table_param.copy_field_end=0;
first_record= sort_and_group=0;
send_records= (ha_rows) 0;
@@ -11625,21 +11657,45 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
return NESTED_LOOP_KILLED; // Aborted by user /* purecov: inspected */
}
SQL_SELECT *select=join_tab->select;
- if (rc == NESTED_LOOP_OK &&
- (!join_tab->cache.select || !join_tab->cache.select->skip_record()))
+ if (rc == NESTED_LOOP_OK)
{
- uint i;
- reset_cache_read(&join_tab->cache);
- for (i=(join_tab->cache.records- (skip_last ? 1 : 0)) ; i-- > 0 ;)
+ bool consider_record= !join_tab->cache.select ||
+ !join_tab->cache.select->skip_record();
+
+ /*
+ Check for error: skip_record() can execute code by calling
+ Item_subselect::val_*. We need to check for errors (if any)
+ after such call.
+ */
+ if (join->thd->is_error())
+ {
+ reset_cache_write(&join_tab->cache);
+ return NESTED_LOOP_ERROR;
+ }
+
+ if (consider_record)
{
- read_cached_record(join_tab);
- if (!select || !select->skip_record())
+ uint i;
+ reset_cache_read(&join_tab->cache);
+ for (i=(join_tab->cache.records- (skip_last ? 1 : 0)) ; i-- > 0 ;)
{
- rc= (join_tab->next_select)(join,join_tab+1,0);
- if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
+ read_cached_record(join_tab);
+ if (!select || !select->skip_record())
{
- reset_cache_write(&join_tab->cache);
- return rc;
+ /*
+ Check for error: skip_record() can execute code by calling
+ Item_subselect::val_*. We need to check for errors (if any)
+ after such call.
+ */
+ if (join->thd->is_error())
+ rc= NESTED_LOOP_ERROR;
+ else
+ rc= (join_tab->next_select)(join,join_tab+1,0);
+ if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
+ {
+ reset_cache_write(&join_tab->cache);
+ return rc;
+ }
}
}
}
@@ -12891,7 +12947,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
key_part_end=key_part+table->key_info[idx].key_parts;
key_part_map const_key_parts=table->const_key_parts[idx];
int reverse=0;
- my_bool on_primary_key= FALSE;
+ my_bool on_pk_suffix= FALSE;
DBUG_ENTER("test_if_order_by_key");
for (; order ; order=order->next, const_key_parts>>=1)
@@ -12913,11 +12969,12 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
key as a suffix to the secondary keys. If it has continue to check
the primary key as a suffix.
*/
- if (!on_primary_key &&
+ if (!on_pk_suffix &&
(table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
- table->s->primary_key != MAX_KEY)
+ table->s->primary_key != MAX_KEY &&
+ table->s->primary_key != idx)
{
- on_primary_key= TRUE;
+ on_pk_suffix= TRUE;
key_part= table->key_info[table->s->primary_key].key_part;
key_part_end=key_part+table->key_info[table->s->primary_key].key_parts;
const_key_parts=table->const_key_parts[table->s->primary_key];
@@ -12949,7 +13006,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
reverse=flag; // Remember if reverse
key_part++;
}
- if (on_primary_key)
+ if (on_pk_suffix)
{
uint used_key_parts_secondary= table->key_info[idx].key_parts;
uint used_key_parts_pk=
@@ -13438,8 +13495,15 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
select_limit= table_records;
if (group)
{
- rec_per_key= used_key_parts ? keyinfo->rec_per_key[used_key_parts-1]
- : 1;
+ /*
+ Used_key_parts can be larger than keyinfo->key_parts
+ when using a secondary index clustered with a primary
+ key (e.g. as in Innodb).
+ See Bug #28591 for details.
+ */
+ rec_per_key= used_key_parts &&
+ used_key_parts <= keyinfo->key_parts ?
+ keyinfo->rec_per_key[used_key_parts-1] : 1;
set_if_bigger(rec_per_key, 1);
/*
With a grouping query each group containing on average
@@ -14617,11 +14681,29 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
We check order_item->fixed because Item_func_group_concat can put
arguments for which fix_fields already was called.
+
+ group_fix_field= TRUE is to resolve aliases from the SELECT list
+ without creating of Item_ref-s: JOIN::exec() wraps aliased items
+ in SELECT list with Item_copy items. To re-evaluate such a tree
+ that includes Item_copy items we have to refresh Item_copy caches,
+ but:
+ - filesort() never refresh Item_copy items,
+ - end_send_group() checks every record for group boundary by the
+ test_if_group_changed function that obtain data from these
+ Item_copy items, but the copy_fields function that
+ refreshes Item copy items is called after group boundaries only -
+ that is a vicious circle.
+ So we prevent inclusion of Item_copy items.
*/
- if (!order_item->fixed &&
+ bool save_group_fix_field= thd->lex->current_select->group_fix_field;
+ if (is_group_field)
+ thd->lex->current_select->group_fix_field= TRUE;
+ bool ret= (!order_item->fixed &&
(order_item->fix_fields(thd, order->item) ||
(order_item= *order->item)->check_cols(1) ||
- thd->is_fatal_error))
+ thd->is_fatal_error));
+ thd->lex->current_select->group_fix_field= save_group_fix_field;
+ if (ret)
return TRUE; /* Wrong field. */
uint el= all_fields.elements;