diff options
Diffstat (limited to 'sql/item_sum.cc')
-rw-r--r-- | sql/item_sum.cc | 202 |
1 files changed, 141 insertions, 61 deletions
diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 75aec7918fa..72e0e637d38 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2008, 2013 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -365,7 +366,19 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref) sl= sl->master_unit()->outer_select() ) sl->master_unit()->item->with_sum_func= 1; } - thd->lex->current_select->mark_as_dependent(aggr_sel); + thd->lex->current_select->mark_as_dependent(thd, aggr_sel, NULL); + return FALSE; +} + + +bool Item_sum::collect_outer_ref_processor(uchar *param) +{ + Collect_deps_prm *prm= (Collect_deps_prm *)param; + SELECT_LEX *ds; + if ((ds= depended_from()) && + ds->nest_level_base == prm->nest_level_base && + ds->nest_level < prm->nest_level) + prm->parameters->add_unique(this, &cmp_items); return FALSE; } @@ -434,6 +447,7 @@ void Item_sum::mark_as_sum_func() cur_select->n_sum_items++; cur_select->with_sum_func= 1; with_sum_func= 1; + with_field= 0; } @@ -500,7 +514,7 @@ bool Item_sum::walk (Item_processor processor, bool walk_subquery, Field *Item_sum::create_tmp_field(bool group, TABLE *table, uint convert_blob_length) { - Field *field; + Field *UNINIT_VAR(field); switch (result_type()) { case REAL_RESULT: field= new Field_double(max_length, maybe_null, name, decimals, TRUE); @@ -520,7 +534,8 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table, field= Field_new_decimal::create_from_item(this); break; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: // This case should never be choosen DBUG_ASSERT(0); return 0; @@ -541,11 +556,20 @@ void Item_sum::update_used_tables () args[i]->update_used_tables(); used_tables_cache|= args[i]->used_tables(); } - - used_tables_cache&= PSEUDO_TABLE_BITS; - - /* the aggregate function is aggregated into its local context */ - used_tables_cache|= ((table_map)1 << aggr_sel->join->tables) - 1; + /* + MariaDB: don't run the following { + + used_tables_cache&= PSEUDO_TABLE_BITS; + + // the aggregate function is aggregated into its local context + used_tables_cache|= ((table_map)1 << aggr_sel->join->tables) - 1; + + } because if we do it, table elimination will assume that + - constructs like "COUNT(*)" use columns from all tables + - so, it is not possible to eliminate any table + our solution for COUNT(*) is that it has + item->used_tables() == 0 && !item->const_item() + */ } } @@ -772,7 +796,7 @@ bool Aggregator_distinct::setup(THD *thd) if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, 0, (select_lex->options | thd->variables.option_bits), - HA_POS_ERROR, ""))) + HA_POS_ERROR, const_cast<char*>("")))) return TRUE; table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows table->no_rows=1; @@ -987,7 +1011,7 @@ bool Aggregator_distinct::add() */ return tree->unique_add(table->record[0] + table->s->null_bytes); } - if ((error= table->file->ha_write_row(table->record[0])) && + if ((error= table->file->ha_write_tmp_row(table->record[0])) && table->file->is_fatal_error(error, HA_CHECK_DUP)) return TRUE; return FALSE; @@ -1065,7 +1089,7 @@ void Aggregator_distinct::endup() { /* go over the tree of distinct keys and calculate the aggregate value */ use_distinct_values= TRUE; - tree->walk(item_sum_distinct_walk, (void*) this); + tree->walk(table, item_sum_distinct_walk, (void*) this); use_distinct_values= FALSE; } /* prevent consecutive recalculations */ @@ -1108,13 +1132,13 @@ Item_sum_num::fix_fields(THD *thd, Item **ref) return TRUE; decimals=0; - maybe_null=0; + maybe_null= sum_func() != COUNT_FUNC; for (uint i=0 ; i < arg_count ; i++) { if (args[i]->fix_fields(thd, args + i) || args[i]->check_cols(1)) return TRUE; set_if_bigger(decimals, args[i]->decimals); - maybe_null |= args[i]->maybe_null; + with_subselect|= args[i]->with_subselect; } result_field=0; max_length=float_length(decimals); @@ -1145,6 +1169,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) (item= args[0])->check_cols(1)) return TRUE; decimals=item->decimals; + with_subselect= args[0]->with_subselect; switch (hybrid_type= item->result_type()) { case INT_RESULT: @@ -1156,7 +1181,8 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) max_length= float_length(decimals); break; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); }; setup_hybrid(args[0], NULL); @@ -1191,17 +1217,31 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) Setup cache/comparator of MIN/MAX functions. When called by the copy_or_same function value_arg parameter contains calculated value of the original MIN/MAX object and it is saved in this object's cache. + + We mark the value and arg_cache with 'RAND_TABLE_BIT' to ensure + that Arg_comparator::compare_datetime() doesn't allocate new + item inside of Arg_comparator. This would cause compare_datetime() + and Item_sum_min::add() to use different values! */ void Item_sum_hybrid::setup_hybrid(Item *item, Item *value_arg) { - value= Item_cache::get_cache(item); + if (!(value= Item_cache::get_cache(item))) + return; value->setup(item); value->store(value_arg); - arg_cache= Item_cache::get_cache(item); + /* Don't cache value, as it will change */ + if (!item->const_item()) + value->set_used_tables(RAND_TABLE_BIT); + if (!(arg_cache= Item_cache::get_cache(item, item->cmp_type()))) + return; arg_cache->setup(item); + /* Don't cache value, as it will change */ + if (!item->const_item()) + arg_cache->set_used_tables(RAND_TABLE_BIT); cmp= new Arg_comparator(); - cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE); + if (cmp) + cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE); collation.set(item->collation); } @@ -1226,14 +1266,17 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table, */ switch (args[0]->field_type()) { case MYSQL_TYPE_DATE: - field= new Field_newdate(maybe_null, name, collation.collation); + field= new Field_newdate(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE, + name, collation.collation); break; case MYSQL_TYPE_TIME: - field= new Field_time(maybe_null, name, collation.collation); + field= new_Field_time(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE, + name, decimals, collation.collation); break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: - field= new Field_datetime(maybe_null, name, collation.collation); + field= new_Field_datetime(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE, + name, decimals, collation.collation); break; default: return Item_sum::create_tmp_field(group, table, convert_blob_length); @@ -1292,13 +1335,14 @@ void Item_sum_sum::fix_length_and_dec() DBUG_ENTER("Item_sum_sum::fix_length_and_dec"); maybe_null=null_value=1; decimals= args[0]->decimals; - switch (args[0]->result_type()) { + switch (args[0]->cast_to_int_type()) { case REAL_RESULT: case STRING_RESULT: hybrid_type= REAL_RESULT; sum= 0.0; break; case INT_RESULT: + case TIME_RESULT: case DECIMAL_RESULT: { /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */ @@ -1312,7 +1356,7 @@ void Item_sum_sum::fix_length_and_dec() break; } case ROW_RESULT: - default: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } DBUG_PRINT("info", ("Type: %s (%d, %d)", @@ -1607,7 +1651,7 @@ double Item_sum_avg::val_real() my_decimal *Item_sum_avg::val_decimal(my_decimal *val) { - my_decimal sum_buff, cnt; + my_decimal cnt; const my_decimal *sum_dec; DBUG_ASSERT(fixed == 1); if (aggr) @@ -1749,7 +1793,8 @@ void Item_sum_variance::fix_length_and_dec() break; } case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } DBUG_PRINT("info", ("Type: REAL_RESULT (%d, %d)", max_length, (int)decimals)); @@ -1966,8 +2011,22 @@ void Item_sum_hybrid::cleanup() void Item_sum_hybrid::no_rows_in_result() { - was_values= FALSE; - clear(); + /* We may be called here twice in case of ref field in function */ + if (was_values) + { + was_values= FALSE; + was_null_value= value->null_value; + clear(); + } +} + +void Item_sum_hybrid::restore_to_before_no_rows_in_result() +{ + if (!was_values) + { + was_values= TRUE; + null_value= value->null_value= was_null_value; + } } @@ -2172,7 +2231,8 @@ void Item_sum_hybrid::reset_field() break; } case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } } @@ -2453,9 +2513,8 @@ Item_sum_hybrid::min_max_update_int_field() void Item_sum_hybrid::min_max_update_decimal_field() { - /* TODO: optimize: do not get result_field in case of args[0] is NULL */ my_decimal old_val, nr_val; - const my_decimal *old_nr= result_field->val_decimal(&old_val); + const my_decimal *old_nr; const my_decimal *nr= args[0]->val_decimal(&nr_val); if (!args[0]->null_value) { @@ -2463,16 +2522,17 @@ Item_sum_hybrid::min_max_update_decimal_field() old_nr=nr; else { + old_nr= result_field->val_decimal(&old_val); bool res= my_decimal_cmp(old_nr, nr) > 0; /* (cmp_sign > 0 && res) || (!(cmp_sign > 0) && !res) */ if ((cmp_sign > 0) ^ (!res)) old_nr=nr; } result_field->set_notnull(); + result_field->store_decimal(old_nr); } else if (result_field->is_null(0)) result_field->set_null(); - result_field->store_decimal(old_nr); } @@ -2646,8 +2706,10 @@ void Item_udf_sum::clear() bool Item_udf_sum::add() { + my_bool tmp_null_value; DBUG_ENTER("Item_udf_sum::add"); - udf.add(&null_value); + udf.add(&tmp_null_value); + null_value= tmp_null_value; DBUG_RETURN(0); } @@ -2683,11 +2745,15 @@ Item *Item_sum_udf_float::copy_or_same(THD* thd) double Item_sum_udf_float::val_real() { + my_bool tmp_null_value; + double res; DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_sum_udf_float::val"); DBUG_PRINT("info",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); - DBUG_RETURN(udf.val(&null_value)); + res= udf.val(&tmp_null_value); + null_value= tmp_null_value; + DBUG_RETURN(res); } @@ -2723,12 +2789,16 @@ longlong Item_sum_udf_decimal::val_int() my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf) { + my_decimal *res; + my_bool tmp_null_value; DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_udf_decimal::val_decimal"); DBUG_PRINT("info",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); - DBUG_RETURN(udf.val_decimal(&null_value, dec_buf)); + res= udf.val_decimal(&tmp_null_value, dec_buf); + null_value= tmp_null_value; + DBUG_RETURN(res); } @@ -2745,11 +2815,15 @@ Item *Item_sum_udf_int::copy_or_same(THD* thd) longlong Item_sum_udf_int::val_int() { + my_bool tmp_null_value; + longlong res; DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_sum_udf_int::val_int"); DBUG_PRINT("info",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); - DBUG_RETURN(udf.val_int(&null_value)); + res= udf.val_int(&tmp_null_value); + null_value= tmp_null_value; + DBUG_RETURN(res); } @@ -2834,13 +2908,12 @@ int group_concat_key_cmp_with_distinct(void* arg, const void* key1, const void* key2) { Item_func_group_concat *item_func= (Item_func_group_concat*)arg; - TABLE *table= item_func->table; for (uint i= 0; i < item_func->arg_count_field; i++) { Item *item= item_func->args[i]; /* - If field_item is a const item then either get_tp_table_field returns 0 + If field_item is a const item then either get_tmp_table_field returns 0 or it is an item over a const table. */ if (item->const_item()) @@ -2852,7 +2925,8 @@ int group_concat_key_cmp_with_distinct(void* arg, const void* key1, */ Field *field= item->get_tmp_table_field(); int res; - uint offset= field->offset(field->table->record[0])-table->s->null_bytes; + uint offset= (field->offset(field->table->record[0]) - + field->table->s->null_bytes); if((res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset))) return res; } @@ -2870,28 +2944,37 @@ int group_concat_key_cmp_with_order(void* arg, const void* key1, { Item_func_group_concat* grp_item= (Item_func_group_concat*) arg; ORDER **order_item, **end; - TABLE *table= grp_item->table; for (order_item= grp_item->order, end=order_item+ grp_item->arg_count_order; order_item < end; order_item++) { Item *item= *(*order_item)->item; + /* + If field_item is a const item then either get_tmp_table_field returns 0 + or it is an item over a const table. + */ + if (item->const_item()) + continue; /* We have to use get_tmp_table_field() instead of real_item()->get_tmp_table_field() because we want the field in the temporary table, not the original field + + Note that for the case of ROLLUP, field may point to another table + tham grp_item->table. This is howver ok as the table definitions are + the same. */ Field *field= item->get_tmp_table_field(); /* - If item is a const item then either get_tp_table_field returns 0 + If item is a const item then either get_tmp_table_field returns 0 or it is an item over a const table. */ - if (field && !item->const_item()) + if (field) { int res; uint offset= (field->offset(field->table->record[0]) - - table->s->null_bytes); + field->table->s->null_bytes); if ((res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset))) return (*order_item)->asc ? res : -res; } @@ -2915,6 +2998,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), { Item_func_group_concat *item= (Item_func_group_concat *) item_arg; TABLE *table= item->table; + uint max_length= table->in_use->variables.group_concat_max_len; String tmp((char *)table->record[1], table->s->reclength, default_charset_info); String tmp2; @@ -2957,7 +3041,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), item->row_count++; /* stop if length of result more than max_length */ - if (result->length() > item->max_length) + if (result->length() > max_length) { int well_formed_error; CHARSET_INFO *cs= item->collation.collation; @@ -2970,7 +3054,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), */ add_length= cs->cset->well_formed_len(cs, ptr + old_length, - ptr + item->max_length, + ptr + max_length, result->length(), &well_formed_error); result->length(old_length + add_length); @@ -3083,19 +3167,19 @@ Item_func_group_concat::Item_func_group_concat(THD *thd, object being copied. */ ORDER *tmp; - if (!(order= (ORDER **) thd->alloc(sizeof(ORDER *) * arg_count_order + - sizeof(ORDER) * arg_count_order))) + if (!(tmp= (ORDER *) thd->alloc(sizeof(ORDER *) * arg_count_order + + sizeof(ORDER) * arg_count_order))) return; - tmp= (ORDER *)(order + arg_count_order); + order= (ORDER **)(tmp + arg_count_order); for (uint i= 0; i < arg_count_order; i++, tmp++) { memcpy(tmp, item->order[i], sizeof(ORDER)); + tmp->next= i == arg_count_order-1 ? 0 : tmp+1; order[i]= tmp; } } - void Item_func_group_concat::cleanup() { DBUG_ENTER("Item_func_group_concat::cleanup"); @@ -3135,19 +3219,11 @@ Field *Item_func_group_concat::make_string_field(TABLE *table) { Field *field; DBUG_ASSERT(collation.collation); - /* - max_characters is maximum number of characters - what can fit into max_length size. It's necessary - to use field size what allows to store group_concat - result without truncation. For this purpose we use - max_characters * CS->mbmaxlen. - */ - const uint32 max_characters= max_length / collation.collation->mbminlen; - if (max_characters > CONVERT_IF_BIGGER_TO_BLOB) - field= new Field_blob(max_characters * collation.collation->mbmaxlen, + if (too_big_for_varchar()) + field= new Field_blob(max_length, maybe_null, name, collation.collation, TRUE); else - field= new Field_varstring(max_characters * collation.collation->mbmaxlen, + field= new Field_varstring(max_length, maybe_null, name, table->s, collation.collation); if (field) @@ -3251,6 +3327,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) args[i]->fix_fields(thd, args + i)) || args[i]->check_cols(1)) return TRUE; + with_subselect|= args[i]->with_subselect; } /* skip charset aggregation for order columns */ @@ -3261,7 +3338,9 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) result.set_charset(collation.collation); result_field= 0; null_value= 1; - max_length= thd->variables.group_concat_max_len; + max_length= thd->variables.group_concat_max_len + / collation.collation->mbminlen + * collation.collation->mbmaxlen; uint32 offset; if (separator->needs_conversion(separator->length(), separator->charset(), @@ -3379,7 +3458,8 @@ bool Item_func_group_concat::setup(THD *thd) */ if (!(table= create_tmp_table(thd, tmp_table_param, all_fields, (ORDER*) 0, 0, TRUE, - (select_lex->options | thd->variables.option_bits), + (select_lex->options | + thd->variables.option_bits), HA_POS_ERROR, (char*) ""))) DBUG_RETURN(TRUE); table->file->extra(HA_EXTRA_NO_ROWS); |