diff options
Diffstat (limited to 'sql/opt_range.cc')
-rw-r--r-- | sql/opt_range.cc | 172 |
1 files changed, 112 insertions, 60 deletions
diff --git a/sql/opt_range.cc b/sql/opt_range.cc index ad48f3050c0..eb6eefccf02 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -343,6 +343,7 @@ typedef struct st_qsel_param { char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH], max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; bool quick; // Don't calulate possible keys + COND *cond; uint fields_bitmap_size; MY_BITMAP needed_fields; /* bitmask of fields needed by the query */ @@ -363,10 +364,11 @@ class TABLE_READ_PLAN; struct st_ror_scan_info; -static SEL_TREE * get_mm_parts(PARAM *param,Field *field, +static SEL_TREE * get_mm_parts(PARAM *param,COND *cond_func,Field *field, Item_func::Functype type,Item *value, Item_result cmp_type); -static SEL_ARG *get_mm_leaf(PARAM *param,Field *field,KEY_PART *key_part, +static SEL_ARG *get_mm_leaf(PARAM *param,COND *cond_func,Field *field, + KEY_PART *key_part, Item_func::Functype type,Item *value); static SEL_TREE *get_mm_tree(PARAM *param,COND *cond); @@ -688,14 +690,25 @@ SQL_SELECT::SQL_SELECT() :quick(0),cond(0),free_cond(0) } -SQL_SELECT::~SQL_SELECT() +void SQL_SELECT::cleanup() { delete quick; + quick= 0; if (free_cond) + { + free_cond=0; delete cond; + cond= 0; + } close_cached_file(&file); } + +SQL_SELECT::~SQL_SELECT() +{ + cleanup(); +} + #undef index // Fix for Unixware 7 QUICK_SELECT_I::QUICK_SELECT_I() @@ -1132,6 +1145,7 @@ SEL_ARG *SEL_ARG::clone(SEL_ARG *new_parent,SEL_ARG **next_arg) return 0; // OOM } increment_use_count(1); + tmp->color= color; return tmp; } @@ -1426,7 +1440,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, param.needed_reg= &needed_reg; param.imerge_cost_buff_size= 0; - thd->no_errors=1; // Don't warn about NULL init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0); @@ -2890,6 +2903,8 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE) DBUG_RETURN(0); // Can't be calculated + param->cond= cond; + if (cond_func->functype() == Item_func::BETWEEN) { if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) @@ -2897,10 +2912,10 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) Field *field=((Item_field*) (cond_func->arguments()[0]))->field; Item_result cmp_type=field->cmp_type(); DBUG_RETURN(tree_and(param, - get_mm_parts(param, field, + get_mm_parts(param, cond_func, field, Item_func::GE_FUNC, cond_func->arguments()[1], cmp_type), - get_mm_parts(param, field, + get_mm_parts(param, cond_func, field, Item_func::LE_FUNC, cond_func->arguments()[2], cmp_type))); } @@ -2913,13 +2928,14 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) { Field *field=((Item_field*) (func->key_item()))->field; Item_result cmp_type=field->cmp_type(); - tree= get_mm_parts(param,field,Item_func::EQ_FUNC, + tree= get_mm_parts(param,cond_func,field,Item_func::EQ_FUNC, func->arguments()[1],cmp_type); if (!tree) DBUG_RETURN(tree); // Not key field for (uint i=2 ; i < func->argument_count(); i++) { - SEL_TREE *new_tree=get_mm_parts(param,field,Item_func::EQ_FUNC, + SEL_TREE *new_tree=get_mm_parts(param,cond_func,field, + Item_func::EQ_FUNC, func->arguments()[i],cmp_type); tree=tree_or(param,tree,new_tree); } @@ -2938,7 +2954,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) /* btw, ft_func's arguments()[0] isn't FIELD_ITEM. SerG*/ if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) { - tree= get_mm_parts(param, + tree= get_mm_parts(param, cond_func, ((Item_field*) (cond_func->arguments()[0]))->field, cond_func->functype(), cond_func->arg_count > 1 ? cond_func->arguments()[1] : @@ -2951,7 +2967,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) cond_func->have_rev_func() && cond_func->arguments()[1]->type() == Item::FIELD_ITEM) { - DBUG_RETURN(get_mm_parts(param, + DBUG_RETURN(get_mm_parts(param, cond_func, ((Item_field*) (cond_func->arguments()[1]))->field, ((Item_bool_func2*) cond_func)->rev_functype(), @@ -2965,7 +2981,8 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) static SEL_TREE * -get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, +get_mm_parts(PARAM *param, COND *cond_func, Field *field, + Item_func::Functype type, Item *value, Item_result cmp_type) { bool ne_func= FALSE; @@ -2994,7 +3011,8 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, DBUG_RETURN(0); // OOM if (!value || !(value->used_tables() & ~param->read_tables)) { - sel_arg=get_mm_leaf(param,key_part->field,key_part,type,value); + sel_arg=get_mm_leaf(param,cond_func, + key_part->field,key_part,type,value); if (!sel_arg) continue; if (sel_arg->type == SEL_ARG::IMPOSSIBLE) @@ -3017,7 +3035,8 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, if (ne_func) { - SEL_TREE *tree2= get_mm_parts(param, field, Item_func::GT_FUNC, + SEL_TREE *tree2= get_mm_parts(param, cond_func, + field, Item_func::GT_FUNC, value, cmp_type); if (tree2) tree= tree_or(param,tree,tree2); @@ -3028,7 +3047,7 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, static SEL_ARG * -get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, +get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, Item_func::Functype type,Item *value) { uint maybe_null=(uint) field->real_maybe_null(), copies; @@ -3037,6 +3056,32 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, char *str, *str2; DBUG_ENTER("get_mm_leaf"); + if (!value) // IS NULL or IS NOT NULL + { + if (field->table->outer_join) // Can't use a key on this + DBUG_RETURN(0); + if (!maybe_null) // Not null field + DBUG_RETURN(type == Item_func::ISNULL_FUNC ? &null_element : 0); + if (!(tree=new SEL_ARG(field,is_null_string,is_null_string))) + DBUG_RETURN(0); // out of memory + if (type == Item_func::ISNOTNULL_FUNC) + { + tree->min_flag=NEAR_MIN; /* IS NOT NULL -> X > NULL */ + tree->max_flag=NO_MAX_RANGE; + } + DBUG_RETURN(tree); + } + + /* + We can't use an index when comparing strings of + different collations + */ + if (field->result_type() == STRING_RESULT && + value->result_type() == STRING_RESULT && + key_part->image_type == Field::itRAW && + ((Field_str*)field)->charset() != conf_func->compare_collation()) + DBUG_RETURN(0); + if (type == Item_func::LIKE_FUNC) { bool like_error; @@ -3084,14 +3129,15 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, max_str[0]= min_str[0]=0; like_error= my_like_range(field->charset(), - res->ptr(),res->length(), - wild_prefix,wild_one,wild_many, - field_length, - min_str+offset, max_str+offset, - &min_length,&max_length); - + res->ptr(), res->length(), + ((Item_func_like*)(param->cond))->escape, + wild_one, wild_many, + field_length, + min_str+offset, max_str+offset, + &min_length, &max_length); if (like_error) // Can't optimize with LIKE DBUG_RETURN(0); + if (offset != maybe_null) // Blob { int2store(min_str+maybe_null,min_length); @@ -3100,22 +3146,6 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, DBUG_RETURN(new SEL_ARG(field,min_str,max_str)); } - if (!value) // IS NULL or IS NOT NULL - { - if (field->table->outer_join) // Can't use a key on this - DBUG_RETURN(0); - if (!maybe_null) // Not null field - DBUG_RETURN(type == Item_func::ISNULL_FUNC ? &null_element : 0); - if (!(tree=new SEL_ARG(field,is_null_string,is_null_string))) - DBUG_RETURN(0); // out of memory - if (type == Item_func::ISNOTNULL_FUNC) - { - tree->min_flag=NEAR_MIN; /* IS NOT NULL -> X > NULL */ - tree->max_flag=NO_MAX_RANGE; - } - DBUG_RETURN(tree); - } - if (!field->optimize_range(param->real_keynr[key_part->key]) && type != Item_func::EQ_FUNC && type != Item_func::EQUAL_FUNC) @@ -3129,8 +3159,8 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, value->result_type() != STRING_RESULT && field->cmp_type() != value->result_type()) DBUG_RETURN(0); - - if (value->save_in_field(field, 1) > 0) + + if (value->save_in_field(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 @@ -3842,6 +3872,8 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) return 0; // OOM tmp->copy_max_to_min(&key); tmp->increment_use_count(key1->use_count+1); + /* Increment key count as it may be used for next loop */ + key.increment_use_count(1); new_arg->next_key_part=key_or(tmp->next_key_part,key.next_key_part); key1=key1->insert(new_arg); break; @@ -4273,6 +4305,7 @@ static ulong count_key_part_usage(SEL_ARG *root, SEL_ARG *key) void SEL_ARG::test_use_count(SEL_ARG *root) { + uint e_count=0; if (this == root && use_count != 1) { sql_print_error("Note: Use_count: Wrong count %lu for root",use_count); @@ -4280,7 +4313,6 @@ void SEL_ARG::test_use_count(SEL_ARG *root) } if (this->type != SEL_ARG::KEY_RANGE) return; - uint e_count=0; for (SEL_ARG *pos=first(); pos ; pos=pos->next) { e_count++; @@ -4297,8 +4329,8 @@ void SEL_ARG::test_use_count(SEL_ARG *root) } } if (e_count != elements) - sql_print_error("Warning: Wrong use count: %u for tree at %lx", e_count, - (gptr) this); + sql_print_error("Warning: Wrong use count: %u (should be %u) for tree at %lx", + e_count, elements, (gptr) this); } #endif @@ -4359,6 +4391,7 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) if (cpk_scan) param->is_ror_scan= true; } + DBUG_PRINT("exit", ("Records: %lu", (ulong) records)); DBUG_RETURN(records); } @@ -4671,9 +4704,9 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, } /* Get range for retrieving rows in QUICK_SELECT::get_next */ - if (!(range= new QUICK_RANGE(param->min_key, + if (!(range= new QUICK_RANGE((const char *) param->min_key, (uint) (tmp_min_key - param->min_key), - param->max_key, + (const char *) param->max_key, (uint) (tmp_max_key - param->max_key), flag))) return 1; // out of memory @@ -4821,8 +4854,30 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, key_part->part_length+=HA_KEY_BLOB_LENGTH; key_part->null_bit= key_info->key_part[part].null_bit; } - if (!insert_dynamic(&quick->ranges,(gptr)&range)) - return quick; + if (insert_dynamic(&quick->ranges,(gptr)&range)) + goto err; + + /* + Add a NULL range if REF_OR_NULL optimization is used. + For example: + if we have "WHERE A=2 OR A IS NULL" we created the (A=2) range above + and have ref->null_ref_key set. Will create a new NULL range here. + */ + if (ref->null_ref_key) + { + QUICK_RANGE *null_range; + + *ref->null_ref_key= 1; // Set null byte then create a range + if (!(null_range= new QUICK_RANGE((char*)ref->key_buff, ref->key_length, + (char*)ref->key_buff, ref->key_length, + EQ_RANGE))) + goto err; + *ref->null_ref_key= 0; // Clear null byte + if (insert_dynamic(&quick->ranges,(gptr)&null_range)) + goto err; + } + + return quick; err: delete quick; @@ -5099,12 +5154,7 @@ int QUICK_RANGE_SELECT::get_next() int result; if (range) { // Already read through key -/* result=((range->flag & EQ_RANGE) ? - file->index_next_same(record, (byte*) range->min_key, - range->min_length) : - file->index_next(record)); -*/ - result=((range->flag & (EQ_RANGE | GEOM_FLAG) ) ? + result=((range->flag & (EQ_RANGE | GEOM_FLAG)) ? file->index_next_same(record, (byte*) range->min_key, range->min_length) : file->index_next(record)); @@ -5360,15 +5410,17 @@ int QUICK_SELECT_DESC::get_next() ((range->flag & NEAR_MAX) ? HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV)); #else - /* Heikki changed Sept 11, 2002: since InnoDB does not store the cursor - position if READ_KEY_EXACT is used to a primary key with all - key columns specified, we must use below HA_READ_KEY_OR_NEXT, - so that InnoDB stores the cursor position and is able to move - the cursor one step backward after the search. */ - - /* Note: even if max_key is only a prefix, HA_READ_AFTER_KEY will - * do the right thing - go past all keys which match the prefix */ - + /* + Heikki changed Sept 11, 2002: since InnoDB does not store the cursor + position if READ_KEY_EXACT is used to a primary key with all + key columns specified, we must use below HA_READ_KEY_OR_NEXT, + so that InnoDB stores the cursor position and is able to move + the cursor one step backward after the search. + */ + /* + Note: even if max_key is only a prefix, HA_READ_AFTER_KEY will + do the right thing - go past all keys which match the prefix + */ result=file->index_read(record, (byte*) range->max_key, range->max_length, ((range->flag & NEAR_MAX) ? |