diff options
Diffstat (limited to 'sql/item_sum.cc')
-rw-r--r-- | sql/item_sum.cc | 321 |
1 files changed, 286 insertions, 35 deletions
diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 5b24a1eda90..431d8b56e6a 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -30,7 +30,7 @@ Item_sum::Item_sum(List<Item> &list) if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { uint i=0; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) @@ -52,6 +52,8 @@ void Item_sum::make_field(Send_field *tmp_field) tmp_field->flags=0; if (!maybe_null) tmp_field->flags|= NOT_NULL_FLAG; + if (unsigned_flag) + tmp_field->flags |= UNSIGNED_FLAG; tmp_field->length=max_length; tmp_field->decimals=decimals; tmp_field->type=(result_type() == INT_RESULT ? FIELD_TYPE_LONG : @@ -150,7 +152,7 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables) return 1; hybrid_type=item->result_type(); if (hybrid_type == INT_RESULT) - max_length=21; + max_length=20; else if (hybrid_type == REAL_RESULT) max_length=float_length(decimals); else @@ -158,6 +160,7 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables) decimals=item->decimals; maybe_null=item->maybe_null; binary=item->binary; + unsigned_flag=item->unsigned_flag; result_field=0; null_value=1; fix_length_and_dec(); @@ -323,12 +326,27 @@ double Item_sum_hybrid::val() { if (null_value) return 0.0; - if (hybrid_type == STRING_RESULT) - { + switch (hybrid_type) { + case STRING_RESULT: String *res; res=val_str(&str_value); return res ? atof(res->c_ptr()) : 0.0; + case INT_RESULT: + if (unsigned_flag) + return ulonglong2double(sum_int); + return (double) sum_int; + case REAL_RESULT: + return sum; } - return sum; + return 0; // Keep compiler happy +} + +longlong Item_sum_hybrid::val_int() +{ + if (null_value) + return 0; + if (hybrid_type == INT_RESULT) + return sum_int; + return (longlong) Item_sum_hybrid::val(); } @@ -337,25 +355,26 @@ Item_sum_hybrid::val_str(String *str) { if (null_value) return 0; - if (hybrid_type == STRING_RESULT) + switch (hybrid_type) { + case STRING_RESULT: return &value; - str->set(sum,decimals); - return str; + case REAL_RESULT: + str->set(sum,decimals); + break; + case INT_RESULT: + if (unsigned_flag) + str->set((ulonglong) sum_int); + else + str->set((longlong) sum_int); + break; + } + return str; // Keep compiler happy } - bool Item_sum_min::add() { - if (hybrid_type != STRING_RESULT) - { - double nr=args[0]->val(); - if (!args[0]->null_value && (null_value || nr < sum)) - { - sum=nr; - null_value=0; - } - } - else + switch (hybrid_type) { + case STRING_RESULT: { String *result=args[0]->val_str(&tmp_value); if (!args[0]->null_value && @@ -366,22 +385,39 @@ bool Item_sum_min::add() null_value=0; } } - return 0; -} - - -bool Item_sum_max::add() -{ - if (hybrid_type != STRING_RESULT) + break; + case INT_RESULT: + { + longlong nr=args[0]->val_int(); + if (!args[0]->null_value && (null_value || + (unsigned_flag && + (ulonglong) nr < (ulonglong) sum_int) || + (!unsigned_flag && nr < sum_int))) + { + sum_int=nr; + null_value=0; + } + } + break; + case REAL_RESULT: { double nr=args[0]->val(); - if (!args[0]->null_value && (null_value || nr > sum)) + if (!args[0]->null_value && (null_value || nr < sum)) { sum=nr; null_value=0; } } - else + break; + } + return 0; +} + + +bool Item_sum_max::add() +{ + switch (hybrid_type) { + case STRING_RESULT: { String *result=args[0]->val_str(&tmp_value); if (!args[0]->null_value && @@ -392,6 +428,31 @@ bool Item_sum_max::add() null_value=0; } } + break; + case INT_RESULT: + { + longlong nr=args[0]->val_int(); + if (!args[0]->null_value && (null_value || + (unsigned_flag && + (ulonglong) nr > (ulonglong) sum_int) || + (!unsigned_flag && nr > sum_int))) + { + sum_int=nr; + null_value=0; + } + } + break; + case REAL_RESULT: + { + double nr=args[0]->val(); + if (!args[0]->null_value && (null_value || nr > sum)) + { + sum=nr; + null_value=0; + } + } + break; + } return 0; } @@ -676,9 +737,17 @@ Item_sum_hybrid::min_max_update_int_field(int offset) nr=args[0]->val_int(); if (!args[0]->null_value) { - if (result_field->is_null(offset) || - (cmp_sign > 0 ? old_nr > nr : old_nr < nr)) + if (result_field->is_null(offset)) old_nr=nr; + else + { + bool res=(unsigned_flag ? + (ulonglong) old_nr > (ulonglong) nr : + old_nr > nr); + /* (cmp_sign > 0 && res) || (!(cmp_sign > 0) && !res) */ + if ((cmp_sign > 0) ^ (!res)) + old_nr=nr; + } result_field->set_notnull(); } else if (result_field->is_null(offset)) @@ -788,11 +857,74 @@ String *Item_std_field::val_str(String *str) #include "sql_select.h" +static int simple_raw_key_cmp(void* arg, byte* key1, byte* key2) +{ + return memcmp(key1, key2, *(uint*) arg); +} + +static int simple_str_key_cmp(void* arg, byte* key1, byte* key2) +{ + return my_sortcmp((char*) key1, (char*) key2, *(uint*) arg); +} + +/* + Did not make this one static - at least gcc gets confused when + I try to declare a static function as a friend. If you can figure + out the syntax to make a static function a friend, make this one + static +*/ + +int composite_key_cmp(void* arg, byte* key1, byte* key2) +{ + Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg; + Field **field = item->table->field; + Field **field_end= field + item->table->fields; + uint32 *lengths=item->field_lengths; + for (; field < field_end; ++field) + { + Field* f = *field; + int len = *lengths++; + int res = f->key_cmp(key1, key2); + if (res) + return res; + key1 += len; + key2 += len; + } + return 0; +} + +/* + helper function for walking the tree when we dump it to MyISAM - + tree_walk will call it for each leaf +*/ + +int dump_leaf(byte* key, uint32 count __attribute__((unused)), + Item_sum_count_distinct* item) +{ + byte* buf = item->table->record[0]; + int error; + /* + The first item->rec_offset bytes are taken care of with + restore_record(table,2) in setup() + */ + memcpy(buf + item->rec_offset, key, item->tree.size_of_element); + if ((error = item->table->file->write_row(buf))) + { + if (error != HA_ERR_FOUND_DUPP_KEY && + error != HA_ERR_FOUND_DUPP_UNIQUE) + return 1; + } + return 0; +} + + Item_sum_count_distinct::~Item_sum_count_distinct() { if (table) free_tmp_table(current_thd, table); delete tmp_table_param; + if (use_tree) + delete_tree(&tree); } @@ -829,22 +961,125 @@ bool Item_sum_count_distinct::setup(THD *thd) tmp_table_param->cleanup(); } if (!(table=create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, - 0, 0, current_lex->options | thd->options))) + 0, 0, + current_lex->select->options | thd->options))) return 1; table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows + table->no_rows=1; + + + // no blobs, otherwise it would be MyISAM + if (table->db_type == DB_TYPE_HEAP) + { + qsort_cmp2 compare_key; + void* cmp_arg; + + // to make things easier for dump_leaf if we ever have to dump to MyISAM + restore_record(table,2); + + if (table->fields == 1) + { + /* + If we have only one field, which is the most common use of + count(distinct), it is much faster to use a simpler key + compare method that can take advantage of not having to worry + about other fields + */ + Field* field = table->field[0]; + switch(field->type()) + { + /* + If we have a string, we must take care of charsets and case + sensitivity + */ + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + compare_key = (qsort_cmp2)(field->binary() ? simple_raw_key_cmp: + simple_str_key_cmp); + break; + default: + /* + Since at this point we cannot have blobs anything else can + be compared with memcmp + */ + compare_key = (qsort_cmp2)simple_raw_key_cmp; + break; + } + key_length = field->pack_length(); + cmp_arg = (void*) &key_length; + rec_offset = 1; + } + else // too bad, cannot cheat - there is more than one field + { + bool all_binary = 1; + Field** field, **field_end; + field_end = (field = table->field) + table->fields; + uint32 *lengths; + if (!(field_lengths= + (uint32*) thd->alloc(sizeof(uint32) * table->fields))) + return 1; + + for (key_length = 0, lengths=field_lengths; field < field_end; ++field) + { + uint32 length= (*field)->pack_length(); + key_length += length; + *lengths++ = length; + if (!(*field)->binary()) + all_binary = 0; // Can't break loop here + } + rec_offset = table->reclength - key_length; + if (all_binary) + { + compare_key = (qsort_cmp2)simple_raw_key_cmp; + cmp_arg = (void*) &key_length; + } + else + { + compare_key = (qsort_cmp2) composite_key_cmp ; + cmp_arg = (void*) this; + } + } + + init_tree(&tree, min(max_heap_table_size, sortbuff_size/16), 0, + key_length, compare_key, 0, NULL, cmp_arg); + use_tree = 1; + + /* + The only time key_length could be 0 is if someone does + count(distinct) on a char(0) field - stupid thing to do, + but this has to be handled - otherwise someone can crash + the server with a DoS attack + */ + max_elements_in_tree = ((key_length) ? max_heap_table_size/key_length : + 1); + } return 0; } +int Item_sum_count_distinct::tree_to_myisam() +{ + if (create_myisam_from_heap(table, tmp_table_param, + HA_ERR_RECORD_FILE_FULL, 1) || + tree_walk(&tree, (tree_walk_action)&dump_leaf, (void*)this, + left_root_right)) + return 1; + delete_tree(&tree); + use_tree = 0; + return 0; +} + void Item_sum_count_distinct::reset() { - if (table) + if (use_tree) + reset_tree(&tree); + else if (table) { table->file->extra(HA_EXTRA_NO_CACHE); table->file->delete_all_rows(); table->file->extra(HA_EXTRA_WRITE_CACHE); - (void) add(); } + (void) add(); } bool Item_sum_count_distinct::add() @@ -859,7 +1094,21 @@ bool Item_sum_count_distinct::add() if ((*field)->is_real_null(0)) return 0; // Don't count NULL - if ((error=table->file->write_row(table->record[0]))) + if (use_tree) + { + /* + If the tree got too big, convert to MyISAM, otherwise insert into the + tree. + */ + if (tree.elements_in_tree > max_elements_in_tree) + { + if(tree_to_myisam()) + return 1; + } + else if (!tree_insert(&tree, table->record[0] + rec_offset, 0)) + return 1; + } + else if ((error=table->file->write_row(table->record[0]))) { if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE) @@ -875,6 +1124,8 @@ longlong Item_sum_count_distinct::val_int() { if (!table) // Empty query return LL(0); + if (use_tree) + return tree.elements_in_tree; table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); return table->file->records; } @@ -897,7 +1148,7 @@ void Item_udf_sum::reset() bool Item_udf_sum::add() { - DBUG_ENTER("Item_udf_sum::reset"); + DBUG_ENTER("Item_udf_sum::add"); udf.add(&null_value); DBUG_RETURN(0); } |