diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 67ac53b564a..3847f6af8b6 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1672,8 +1672,9 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, if (!head->used_keys.is_clear_all()) { int key_for_use= find_shortest_key(head, &head->used_keys); - double key_read_time= get_index_only_read_time(¶m, records, - key_for_use); + double key_read_time= (get_index_only_read_time(¶m, records, + key_for_use) + + (double) records / TIME_FOR_COMPARE); DBUG_PRINT("info", ("'all'+'using index' scan will be using key %d, " "read time %g", key_for_use, key_read_time)); if (key_read_time < read_time) @@ -2176,6 +2177,12 @@ skip_to_ror_scan: assumed that each time we read the next key from the index, the handler performs a random seek, thus the cost is proportional to the number of blocks read. + + TODO: + Move this to handler->read_time() by adding a flag 'index-only-read' to + this call. The reason for doing this is that the current function doesn't + handle the case when the row is stored in the b-tree (like in innodb + clustered index) */ inline double get_index_only_read_time(const PARAM* param, ha_rows records, @@ -2190,6 +2197,7 @@ inline double get_index_only_read_time(const PARAM* param, ha_rows records, return read_time; } + typedef struct st_ror_scan_info { uint idx; /* # of used key in param->keys */ @@ -3056,22 +3064,24 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, tree->n_ror_scans++; tree->ror_scans_map.set_bit(idx); } + double cpu_cost= (double) found_records / TIME_FOR_COMPARE; if (found_records != HA_POS_ERROR && found_records > 2 && read_index_only && (param->table->file->index_flags(keynr, param->max_key_part,1) & HA_KEYREAD_ONLY) && !(pk_is_clustered && keynr == param->table->primary_key)) /* We can resolve this by only reading through this key. */ - found_read_time= get_index_only_read_time(param,found_records,keynr); + found_read_time= get_index_only_read_time(param,found_records,keynr) + + cpu_cost; else /* cost(read_through_index) = cost(disk_io) + cost(row_in_range_checks) The row_in_range check is in QUICK_RANGE_SELECT::cmp_next function. */ - found_read_time= (param->table->file->read_time(keynr, - param->range_count, - found_records)+ - (double) found_records / TIME_FOR_COMPARE); + found_read_time= param->table->file->read_time(keynr, + param->range_count, + found_records) + + cpu_cost; DBUG_PRINT("info",("read_time: %g found_read_time: %g", read_time, found_read_time)); @@ -3424,6 +3434,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, { uint maybe_null=(uint) field->real_maybe_null(), copies; uint field_length=field->pack_length()+maybe_null; + bool optimize_range; SEL_ARG *tree; char *str, *str2; DBUG_ENTER("get_mm_leaf"); @@ -3454,6 +3465,9 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, ((Field_str*)field)->charset() != conf_func->compare_collation()) DBUG_RETURN(0); + optimize_range= field->optimize_range(param->real_keynr[key_part->key], + key_part->part); + if (type == Item_func::LIKE_FUNC) { bool like_error; @@ -3461,8 +3475,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, String tmp(buff1,sizeof(buff1),value->collation.collation),*res; uint length,offset,min_length,max_length; - if (!field->optimize_range(param->real_keynr[key_part->key], - key_part->part)) + if (!optimize_range) DBUG_RETURN(0); // Can't optimize this if (!(res= value->val_str(&tmp))) DBUG_RETURN(&null_element); @@ -3527,8 +3540,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, DBUG_RETURN(new SEL_ARG(field,min_str,max_str)); } - if (!field->optimize_range(param->real_keynr[key_part->key], - key_part->part) && + if (!optimize_range && type != Item_func::EQ_FUNC && type != Item_func::EQUAL_FUNC) DBUG_RETURN(0); // Can't optimize this @@ -3542,7 +3554,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, field->cmp_type() != value->result_type()) DBUG_RETURN(0); - if (value->save_in_field(field, 1) < 0) + if (value->save_in_field_no_warnings(field, 1) < 0) { /* This happens when we try to insert a NULL field in a not null column */ DBUG_RETURN(&null_element); // cmp with NULL is never TRUE @@ -3736,14 +3748,15 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) if (*key2 && !(*key2)->simple_key()) flag|=CLONE_KEY2_MAYBE; *key1=key_and(*key1,*key2,flag); - if ((*key1)->type == SEL_ARG::IMPOSSIBLE) + if (*key1 && (*key1)->type == SEL_ARG::IMPOSSIBLE) { tree1->type= SEL_TREE::IMPOSSIBLE; DBUG_RETURN(tree1); } result_keys.set_bit(key1 - tree1->keys); #ifdef EXTRA_DEBUG - (*key1)->test_use_count(*key1); + if (*key1) + (*key1)->test_use_count(*key1); #endif } } @@ -3974,6 +3987,13 @@ key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag) return key1; } + if ((key1->min_flag | key2->min_flag) & GEOM_FLAG) + { + key1->free_tree(); + key2->free_tree(); + return 0; // Can't optimize this + } + key1->use_count--; key2->use_count--; SEL_ARG *e1=key1->first(), *e2=key2->first(), *new_tree=0; @@ -4056,7 +4076,8 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) key1->use_count--; key2->use_count--; - if (key1->part != key2->part) + if (key1->part != key2->part || + (key1->min_flag | key2->min_flag) & GEOM_FLAG) { key1->free_tree(); key2->free_tree(); @@ -4716,7 +4737,7 @@ void SEL_ARG::test_use_count(SEL_ARG *root) ulong count=count_key_part_usage(root,pos->next_key_part); if (count > pos->next_key_part->use_count) { - sql_print_error("Note: Use_count: Wrong count for key at %lx, %lu should be %lu", + sql_print_error("Note: Use_count: Wrong count for key at 0x%lx, %lu should be %lu", pos,pos->next_key_part->use_count,count); return; } @@ -4724,7 +4745,7 @@ void SEL_ARG::test_use_count(SEL_ARG *root) } } if (e_count != elements) - sql_print_error("Warning: Wrong use count: %u (should be %u) for tree at %lx", + sql_print_error("Warning: Wrong use count: %u (should be %u) for tree at 0x%lx", e_count, elements, (gptr) this); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d90f2eeef18..ed65c8fd8fa 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -226,27 +226,13 @@ int handle_select(THD *thd, LEX *lex, select_result *result) thd->net.report_error)); if (thd->net.report_error) res= 1; - if (res > 0) + if (unlikely(res)) { - if (result) - { + if (res > 0) result->send_error(0, NullS); - result->abort(); - } - else - send_error(thd, 0, NullS); + result->abort(); res= 1; // Error sent to client } - if (res < 0) - { - if (result) - { - result->abort(); - } - res= 1; - } - if (result != lex->result) - delete result; DBUG_RETURN(res); } @@ -349,9 +335,7 @@ JOIN::prepare(Item ***rref_pointer_array, if ((subselect= select_lex->master_unit()->item)) { Item_subselect::trans_res res; - if ((res= ((!thd->lex->view_prepare_mode) ? - subselect->select_transformer(this) : - subselect->no_select_transform())) != + if ((res= subselect->select_transformer(this)) != Item_subselect::RES_OK) { select_lex->fix_prepare_information(thd, &conds); @@ -553,6 +537,7 @@ JOIN::optimize() if (cond_value == Item::COND_FALSE || (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS))) { /* Impossible cond */ + DBUG_PRINT("info", ("Impossible WHERE")); zero_result_cause= "Impossible WHERE"; error= 0; DBUG_RETURN(0); @@ -570,20 +555,24 @@ JOIN::optimize() { if (res > 1) { + DBUG_PRINT("error",("Error from opt_sum_query")); DBUG_RETURN(1); } if (res < 0) { + DBUG_PRINT("info",("No matching min/max row")); zero_result_cause= "No matching min/max row"; error=0; DBUG_RETURN(0); } + DBUG_PRINT("info",("Select tables optimized away")); zero_result_cause= "Select tables optimized away"; tables_list= 0; // All tables resolved } } if (!tables_list) { + DBUG_PRINT("info",("No tables")); error= 0; DBUG_RETURN(0); } @@ -777,6 +766,10 @@ JOIN::optimize() (select_lex->ftfunc_list->elements ? SELECT_NO_JOIN_CACHE : 0)); + /* Perform FULLTEXT search before all regular searches */ + if (!(select_options & SELECT_DESCRIBE)) + init_ftfuncs(thd, select_lex, test(order)); + /* is this simple IN subquery? */ @@ -832,7 +825,7 @@ JOIN::optimize() join_tab->info= "Using index; Using where"; else join_tab->info= "Using index"; - + DBUG_RETURN(unit->item-> change_engine(new subselect_indexsubquery_engine(thd, join_tab, @@ -869,7 +862,7 @@ JOIN::optimize() as in other cases the join is done before the sort. */ if (const_tables != tables && - (order || group_list) && + (order || group_list) && join_tab[const_tables].type != JT_ALL && join_tab[const_tables].type != JT_FT && join_tab[const_tables].type != JT_REF_OR_NULL && @@ -883,8 +876,7 @@ JOIN::optimize() ((group_list && const_tables != tables && (!simple_group || !test_if_skip_sort_order(&join_tab[const_tables], group_list, - unit->select_limit_cnt, - 0))) || + unit->select_limit_cnt, 0))) || select_distinct) && tmp_table_param.quick_group && !procedure) { @@ -899,8 +891,6 @@ JOIN::optimize() } having= 0; - /* Perform FULLTEXT search before all regular searches */ - init_ftfuncs(thd, select_lex, test(order)); /* Create a tmp table if distinct or if the sort is too complicated */ if (need_tmp) { @@ -908,7 +898,7 @@ JOIN::optimize() thd->proc_info="Creating tmp table"; init_items_ref_array(); - + tmp_table_param.hidden_field_count= (all_fields.elements - fields_list.elements); if (!(exec_tmp_table1 = @@ -2446,7 +2436,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, } else if (old->eq_func && new_fields->eq_func && old->val->eq(new_fields->val, old->field->binary())) - + { old->level= and_level; old->optimize= ((old->optimize & new_fields->optimize & @@ -2505,7 +2495,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, field Field used in comparision eq_func True if we used =, <=> or IS NULL value Value used for comparison with field - Is NULL for BETWEEN and IN + Is NULL for BETWEEN and IN usable_tables Tables which can be used for key optimization NOTES @@ -2572,7 +2562,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond, bool is_const=1; for (uint i=0; iconst_item(); */ is_const&= (*value)->const_item(); @@ -2583,22 +2573,32 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond, number. cmp_type() is checked to allow compare of dates to numbers. eq_func is NEVER true when num_values > 1 */ - if (!eq_func || - field->result_type() == STRING_RESULT && - (*value)->result_type() != STRING_RESULT && - field->cmp_type() != (*value)->result_type()) - return; - - /* - We can't use indexes if the effective collation - of the operation differ from the field collation. - */ - if (field->result_type() == STRING_RESULT && - (*value)->result_type() == STRING_RESULT && - field->cmp_type() == STRING_RESULT && - ((Field_str*)field)->charset() != cond->compare_collation()) - return; + if (!eq_func) + return; + if (field->result_type() == STRING_RESULT) + { + if ((*value)->result_type() != STRING_RESULT) + { + if (field->cmp_type() != (*value)->result_type()) + return; + } + else + { + /* + We can't use indexes if the effective collation + of the operation differ from the field collation. + We also cannot use index on a text column, as the column may + contain 'x' 'x\t' 'x ' and 'read_next_same' will stop after + 'x' when searching for WHERE col='x ' + */ + if (field->cmp_type() == STRING_RESULT && + (((Field_str*)field)->charset() != cond->compare_collation() || + ((*value)->type() != Item::NULL_ITEM && + (field->flags & BLOB_FLAG) && !field->binary()))) + return; + } + } } } DBUG_ASSERT(num_values == 1); @@ -2701,7 +2701,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) { Item *tmp=new Item_null; - if (!tmp) // Should never be true + if (unlikely(!tmp)) // Should never be true return; add_key_field(key_fields,*and_level,cond_func, ((Item_field*) (cond_func->arguments()[0])->real_item()) @@ -4135,7 +4135,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, rec= keyuse->ref_table_rows; /* If there is one 'key_column IS NULL' expression, we can - use this ref_or_null optimsation of this field + use this ref_or_null optimisation of this field */ found_ref_or_null|= (keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL); @@ -4652,6 +4652,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, store_key **ref_key= j->ref.key_copy; byte *key_buff=j->ref.key_buff, *null_ref_key= 0; + bool keyuse_uses_no_tables= TRUE; if (ftkey) { j->ref.items[0]=((Item_func*)(keyuse->val))->key_item(); @@ -4671,6 +4672,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, uint maybe_null= test(keyinfo->key_part[i].null_bit); j->ref.items[i]=keyuse->val; // Save for cond removal + keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables; if (!keyuse->used_tables && !(join->select_options & SELECT_DESCRIBE)) { // Compare against constant @@ -4710,7 +4712,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, j->type= null_ref_key ? JT_REF_OR_NULL : JT_REF; j->ref.null_ref_key= null_ref_key; } - else if (ref_key == j->ref.key_copy) + else if (keyuse_uses_no_tables) { /* This happen if we are using a constant expression in the ON part @@ -4971,10 +4973,32 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) COND *const_cond= make_cond_for_table(cond,join->const_table_map,(table_map) 0); DBUG_EXECUTE("where",print_where(const_cond,"constants");); + for (JOIN_TAB *tab= join->join_tab+join->const_tables; + tab < join->join_tab+join->tables ; tab++) + { + if (tab->on_expr) + { + JOIN_TAB *cond_tab= tab->first_inner; + COND *tmp= make_cond_for_table(tab->on_expr, + join->const_table_map, + (table_map) 0); + if (!tmp) + continue; + tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl); + if (!tmp) + DBUG_RETURN(1); + tmp->quick_fix_field(); + cond_tab->select_cond= !cond_tab->select_cond ? tmp : + new Item_cond_and(cond_tab->select_cond,tmp); + if (!cond_tab->select_cond) + DBUG_RETURN(1); + cond_tab->select_cond->quick_fix_field(); + } + } if (const_cond && !const_cond->val_int()) { DBUG_PRINT("info",("Found impossible WHERE condition")); - DBUG_RETURN(1); // Impossible const condition + DBUG_RETURN(1); // Impossible const condition } } } @@ -4985,13 +5009,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) JOIN_TAB *tab=join->join_tab+i; JOIN_TAB *first_inner_tab= tab->first_inner; table_map current_map= tab->table->map; + bool use_quick_range=0; /* Following force including random expression in last table condition. It solve problem with select like SELECT * FROM t1 WHERE rand() > 0.5 */ if (i == join->tables-1) current_map|= OUTER_REF_TABLE_BIT | RAND_TABLE_BIT; - bool use_quick_range=0; used_tables|=current_map; if (tab->type == JT_REF && tab->quick && @@ -5012,15 +5036,30 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) tmp= make_cond_for_table(cond,used_tables,current_map); if (cond && !tmp && tab->quick) { // Outer join - /* - Hack to handle the case where we only refer to a table - in the ON part of an OUTER JOIN. - */ - tmp=new Item_int((longlong) 1,1); // Always true + if (tab->type != JT_ALL) + { + /* + Don't use the quick method + We come here in the case where we have 'key=constant' and + the test is removed by make_cond_for_table() + */ + delete tab->quick; + tab->quick= 0; + } + else + { + /* + Hack to handle the case where we only refer to a table + in the ON part of an OUTER JOIN. In this case we want the code + below to check if we should use 'quick' instead. + */ + tmp= new Item_int((longlong) 1,1); // Always true + } + } if (tmp || !cond) { - 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*) join->thd->memdup((gptr) select, sizeof(SQL_SELECT)); if (!sel) @@ -5030,7 +5069,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) add a match guard to the pushed down predicate. The guard will turn the predicate on only after the first match for outer tables is encountered. - */ + */ if (cond) {/* Because of QUICK_GROUP_MIN_MAX_SELECT there may be a select without @@ -5217,6 +5256,7 @@ static void make_join_readinfo(JOIN *join, uint options) { uint i; + bool statistics= test(!(join->select_options & SELECT_DESCRIBE)); DBUG_ENTER("make_join_readinfo"); @@ -5315,7 +5355,8 @@ make_join_readinfo(JOIN *join, uint options) join->thd->server_status|=SERVER_QUERY_NO_GOOD_INDEX_USED; tab->read_first_record= join_init_quick_read_record; if (statistics) - statistic_increment(select_range_check_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_range_check_count, + &LOCK_status); } else { @@ -5325,13 +5366,15 @@ make_join_readinfo(JOIN *join, uint options) if (tab->select && tab->select->quick) { if (statistics) - statistic_increment(select_range_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_range_count, + &LOCK_status); } else { join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED; if (statistics) - statistic_increment(select_scan_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_scan_count, + &LOCK_status); } } else @@ -5339,13 +5382,15 @@ make_join_readinfo(JOIN *join, uint options) if (tab->select && tab->select->quick) { if (statistics) - statistic_increment(select_full_range_join_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_full_range_join_count, + &LOCK_status); } else { join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED; if (statistics) - statistic_increment(select_full_join_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_full_join_count, + &LOCK_status); } } if (!table->no_keyread) @@ -5520,6 +5565,10 @@ JOIN::join_free(bool full) if (full) { group_fields.delete_elements(); + /* + We can't call delete_elements() on copy_funcs as this will cause + problems in free_elements() as some of the elements are then deleted. + */ tmp_table_param.copy_funcs.empty(); tmp_table_param.cleanup(); } @@ -5704,7 +5753,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, bool *simple_order) } if ((ref=order_tables & (not_const_tables ^ first_table))) { - if (only_eq_ref_tables(join,first_order,ref)) + if (!(order_tables & first_table) && only_eq_ref_tables(join,first_order,ref)) { DBUG_PRINT("info",("removing: %s", order->item[0]->full_name())); continue; @@ -5827,7 +5876,10 @@ change_cond_ref_to_const(I_List *save_list,Item *and_father, Item *right_item= func->arguments()[1]; Item_func::Functype functype= func->functype(); - if (right_item->eq(field,0) && left_item != value) + if (right_item->eq(field,0) && left_item != value && + (left_item->result_type() != STRING_RESULT || + value->result_type() != STRING_RESULT || + left_item->collation.collation == value->collation.collation)) { Item *tmp=value->new_item(); if (tmp) @@ -5845,7 +5897,10 @@ change_cond_ref_to_const(I_List *save_list,Item *and_father, func->set_cmp_func(); } } - else if (left_item->eq(field,0) && right_item != value) + else if (left_item->eq(field,0) && right_item != value && + (right_item->result_type() != STRING_RESULT || + value->result_type() != STRING_RESULT || + right_item->collation.collation == value->collation.collation)) { Item *tmp=value->new_item(); if (tmp) @@ -5965,62 +6020,6 @@ propagate_cond_constants(I_List *save_list,COND *and_father, } -/* - Eliminate NOT functions from the condition tree. - - SYNPOSIS - eliminate_not_funcs() - thd thread handler - cond condition tree - - DESCRIPTION - Eliminate NOT functions from the condition tree where it's possible. - Recursively traverse condition tree to find all NOT functions. - Call neg_transformer() method for negated arguments. - - NOTE - If neg_transformer() returned a new condition we call fix_fields(). - We don't delete any items as it's not needed. They will be deleted - later at once. - - RETURN - New condition tree -*/ - -COND *eliminate_not_funcs(THD *thd, COND *cond) -{ - DBUG_ENTER("eliminate_not_funcs"); - - if (!cond) - DBUG_RETURN(cond); - if (cond->type() == Item::COND_ITEM) /* OR or AND */ - { - List_iterator li(*((Item_cond*) cond)->argument_list()); - Item *item; - while ((item= li++)) - { - Item *new_item= eliminate_not_funcs(thd, item); - if (item != new_item) - VOID(li.replace(new_item)); /* replace item with a new condition */ - } - } - else if (cond->type() == Item::FUNC_ITEM && /* 'NOT' operation? */ - ((Item_func*) cond)->functype() == Item_func::NOT_FUNC) - { - COND *new_cond= ((Item_func*) cond)->arguments()[0]->neg_transformer(thd); - if (new_cond) - { - /* - Here we can delete the NOT function. Something like: delete cond; - But we don't need to do it. All items will be deleted later at once. - */ - cond= new_cond; - } - } - DBUG_RETURN(cond); -} - - /* Simplify joins replacing outer joins by inner joins whenever it's possible @@ -6085,9 +6084,9 @@ COND *eliminate_not_funcs(THD *thd, COND *cond) The function removes all unnecessary braces from the expression produced by the conversions. - E.g. SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a t3.b=t1.b + E.g. SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b finally is converted to: - SELECT * FROM t1, t2, t3 WHERE t2.c < 5 AND t2.a=t1.a t3.b=t1.b + SELECT * FROM t1, t2, t3 WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b It also will remove braces from the following queries: SELECT * from (t1 LEFT JOIN t2 ON t2.a=t1.a) LEFT JOIN t3 ON t3.b=t2.b @@ -6122,6 +6121,7 @@ simplify_joins(JOIN *join, List *join_list, COND *conds, bool top) NESTED_JOIN *nested_join; TABLE_LIST *prev_table= 0; List_iterator li(*join_list); + DBUG_ENTER("simplify_joins"); /* Try to simplify join operations from join_list. @@ -6255,35 +6255,37 @@ simplify_joins(JOIN *join, List *join_list, COND *conds, bool top) li.replace(nested_join->join_list); } } - return conds; + DBUG_RETURN(conds); } - + + static COND * optimize_cond(JOIN *join, COND *conds, Item::cond_result *cond_value) { - DBUG_ENTER("optimize_cond"); - THD *thd= join->thd; SELECT_LEX *select= thd->lex->current_select; + DBUG_ENTER("optimize_cond"); + if (select->first_cond_optimization) { - Item_arena *arena= thd->current_arena; - Item_arena backup; - if (arena) + /* + The following code will allocate the new items in a permanent + MEMROOT for prepared statements and stored procedures. + */ + + Item_arena *arena= thd->current_arena, backup; + if (arena->is_conventional()) + arena= 0; // For easier test + else thd->set_n_backup_item_arena(arena, &backup); - if (conds) - { - DBUG_EXECUTE("where",print_where(conds,"original");); - /* eliminate NOT operators */ - conds= eliminate_not_funcs(thd, conds); - } + select->first_cond_optimization= 0; /* Convert all outer joins to inner joins if possible */ conds= simplify_joins(join, join->join_list, conds, TRUE); select->prep_where= conds ? conds->copy_andor_structure(thd) : 0; - select->first_cond_optimization= 0; + if (arena) thd->restore_backup_item_arena(arena, &backup); } @@ -6291,19 +6293,21 @@ optimize_cond(JOIN *join, COND *conds, Item::cond_result *cond_value) if (!conds) { *cond_value= Item::COND_TRUE; - DBUG_RETURN(conds); + select->prep_where= 0; + } + else + { + DBUG_EXECUTE("where", print_where(conds, "original");); + /* change field = field to field = const for each found field = const */ + propagate_cond_constants((I_List *) 0,conds,conds); + /* + Remove all instances of item == item + Remove all and-levels where CONST item != CONST item + */ + DBUG_EXECUTE("where",print_where(conds,"after const change");); + conds= remove_eq_conds(thd, conds, cond_value) ; + DBUG_EXECUTE("info",print_where(conds,"after remove");); } - - DBUG_EXECUTE("where", print_where(conds, "after negation elimination");); - /* change field = field to field = const for each found field = const */ - propagate_cond_constants((I_List *) 0,conds,conds); - /* - Remove all instances of item == item - Remove all and-levels where CONST item != CONST item - */ - DBUG_EXECUTE("where",print_where(conds,"after const change");); - conds= remove_eq_conds(thd, conds, cond_value) ; - DBUG_EXECUTE("info",print_where(conds,"after remove");); DBUG_RETURN(conds); } @@ -6631,7 +6635,7 @@ static Field* create_tmp_field_from_item(THD *thd, copy_func If set and item is a function, store copy of item in this array from_field if field will be created using other field as example, - pointer example field will be written here + pointer example field will be written here group 1 if we are going to do a relative group by on result modify_item 1 if item->result_field should point to new item. This is relevent for how fill_record() is going to @@ -6640,7 +6644,7 @@ static Field* create_tmp_field_from_item(THD *thd, the record in the original table. If modify_item is 0 then fill_record() will update the temporary table - + RETURN 0 on error new_created field @@ -6664,13 +6668,13 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, return new Field_double(item_sum->max_length,maybe_null, item->name, table, item_sum->decimals); case Item_sum::VARIANCE_FUNC: /* Place for sum & count */ - case Item_sum::STD_FUNC: + case Item_sum::STD_FUNC: if (group) return new Field_string(sizeof(double)*2+sizeof(longlong), 0, item->name,table,&my_charset_bin); else return new Field_double(item_sum->max_length, maybe_null, - item->name,table,item_sum->decimals); + item->name,table,item_sum->decimals); case Item_sum::UNIQUE_USERS_FUNC: return new Field_long(9,maybe_null,item->name,table,1); default: @@ -6767,7 +6771,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, (int) distinct, (int) save_sum_fields, (ulong) rows_limit,test(group))); - statistic_increment(created_tmp_tables, &LOCK_status); + statistic_increment(thd->status_var.created_tmp_tables, &LOCK_status); if (use_temp_pool) temp_pool_slot = bitmap_set_next(&temp_pool); @@ -6778,7 +6782,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, else // if we run out of slots or we are not using tempool sprintf(path,"%s%s%lx_%lx_%x",mysql_tmpdir,tmp_file_prefix,current_pid, thd->thread_id, thd->tmp_table++); - + if (lower_case_table_names) my_casedn_str(files_charset_info, path); @@ -6894,14 +6898,21 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, tmp_from_field++; *(reg_field++)= new_field; reclength+=new_field->pack_length(); - if (!(new_field->flags & NOT_NULL_FLAG)) - null_count++; if (new_field->flags & BLOB_FLAG) { *blob_field++= new_field; blob_count++; } ((Item_sum*) item)->args[i]= new Item_field(new_field); + if (!(new_field->flags & NOT_NULL_FLAG)) + { + null_count++; + /* + new_field->maybe_null() is still false, it will be + changed below. But we have to setup Item_field correctly + */ + ((Item_sum*) item)->args[i]->maybe_null=1; + } } } } @@ -7346,7 +7357,8 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, table->db_stat=0; goto err; } - statistic_increment(created_tmp_disk_tables, &LOCK_status); + statistic_increment(table->in_use->status_var.created_tmp_disk_tables, + &LOCK_status); table->db_record_offset=1; DBUG_RETURN(0); err: @@ -7434,6 +7446,18 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, new_table.no_rows=1; } +#ifdef TO_BE_DONE_LATER_IN_4_1 + /* + To use start_bulk_insert() (which is new in 4.1) we need to find + all places where a corresponding end_bulk_insert() should be put. + */ + table->file->info(HA_STATUS_VARIABLE); /* update table->file->records */ + new_table.file->start_bulk_insert(table->file->records); +#else + /* HA_EXTRA_WRITE_CACHE can stay until close, no need to disable it */ + new_table.file->extra(HA_EXTRA_WRITE_CACHE); +#endif + /* copy all old rows */ while (!table->file->rnd_next(new_table.record[1])) { @@ -8146,6 +8170,19 @@ join_read_system(JOIN_TAB *tab) } +/* + Read a table when there is at most one matching row + + SYNOPSIS + join_read_const() + tab Table to read + + RETURN + 0 Row was found + -1 Row was not found + 1 Got an error (other than row not found) during read +*/ + static int join_read_const(JOIN_TAB *tab) { @@ -8153,6 +8190,7 @@ join_read_const(JOIN_TAB *tab) TABLE *table= tab->table; if (table->status & STATUS_GARBAGE) // If first read { + table->status= 0; if (cp_buffer_from_ref(&tab->ref)) error=HA_ERR_KEY_NOT_FOUND; else @@ -8163,6 +8201,7 @@ join_read_const(JOIN_TAB *tab) } if (error) { + table->status= STATUS_NOT_FOUND; table->null_row=1; empty_record(table); if (error != HA_ERR_KEY_NOT_FOUND) @@ -9378,9 +9417,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, keys.merge(table->used_keys); /* - We are adding here also the index speified in FORCE INDEX clause, + We are adding here also the index specified in FORCE INDEX clause, if any. - This is to allow users to use index in ORDER BY. + This is to allow users to use index in ORDER BY. */ if (table->force_index) keys.merge(table->keys_in_use_for_query); @@ -10157,7 +10196,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, Item *itemptr=*order->item; if (itemptr->type() == Item::INT_ITEM) { /* Order by position */ - uint count= itemptr->val_int(); + uint count= (uint) itemptr->val_int(); if (!count || count > fields.elements) { my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR), @@ -10165,18 +10204,25 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, thd->where); return 1; } - order->item= ref_pointer_array + count-1; + order->item= ref_pointer_array + count - 1; order->in_field_list= 1; + order->counter= count; + order->counter_used= 1; return 0; } uint counter; - Item **item= find_item_in_list(itemptr, fields, &counter, IGNORE_ERRORS); - if (item) + Item **item= find_item_in_list(itemptr, fields, &counter, + REPORT_EXCEPT_NOT_FOUND); + if (!item) + return 1; + + if (item != (Item **)not_found_item) { order->item= ref_pointer_array + counter; order->in_field_list=1; return 0; } + order->in_field_list=0; Item *it= *order->item; /* @@ -10639,7 +10685,16 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, { if (!(pos= new Item_copy_string(pos))) goto err; - if (param->copy_funcs.push_back(pos)) + /* + Item_copy_string::copy for function can call + Item_copy_string::val_int for blob via Item_ref. + But if Item_copy_string::copy for blob isn't called before, + it's value will be wrong + so let's insert Item_copy_string for blobs in the beginning of + copy_funcs + (to see full test case look at having.test, BUG #4358) + */ + if (param->copy_funcs.push_front(pos)) goto err; } else @@ -11676,7 +11731,7 @@ int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) SYNOPSIS print_join() thd thread handler - str string where table should bbe printed + str string where table should be printed tables list of tables in join */ @@ -11732,30 +11787,31 @@ void st_table_list::print(THD *thd, String *str) print_join(thd, str, &nested_join->join_list); str->append(')'); } - else if (view_name.str) - { - append_identifier(thd, str, view_db.str, view_db.length); - str->append('.'); - append_identifier(thd, str, view_name.str, view_name.length); - if (my_strcasecmp(table_alias_charset, view_name.str, alias)) - { - str->append(' '); - append_identifier(thd, str, alias, strlen(alias)); - } - } - else if (derived) - { - str->append('('); - derived->print(str); - str->append(") ", 2); - append_identifier(thd, str, alias, strlen(alias)); - } else { - append_identifier(thd, str, db, db_length); - str->append('.'); - append_identifier(thd, str, real_name, real_name_length); - if (my_strcasecmp(table_alias_charset, real_name, alias)) + const char *cmp_name; // Name to compare with alias + if (view_name.str) + { + append_identifier(thd, str, view_db.str, view_db.length); + str->append('.'); + append_identifier(thd, str, view_name.str, view_name.length); + cmp_name= view_name.str; + } + else if (derived) + { + str->append('('); + derived->print(str); + str->append(')'); + cmp_name= ""; // Force printing of alias + } + else + { + append_identifier(thd, str, db, db_length); + str->append('.'); + append_identifier(thd, str, real_name, real_name_length); + cmp_name= real_name; + } + if (my_strcasecmp(table_alias_charset, cmp_name, alias)) { str->append(' '); append_identifier(thd, str, alias, strlen(alias)); @@ -11771,7 +11827,7 @@ void st_select_lex::print(THD *thd, String *str) str->append("select ", 7); - //options + /* First add options */ if (options & SELECT_STRAIGHT_JOIN) str->append("straight_join ", 14); if ((thd->lex->lock_option == TL_READ_HIGH_PRIORITY) &&