mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Fix for BUG#11185.
The source of the problem is in Field_longlong::cmp. If 'this' is an unsigned number, the method casts both the current value, and the constant that we compare with to an unsigned number. As a result if the constant we compare with is a negative number, it wraps to some unsigned number, and the comparison is incorrect. When the optimizer chooses the "range" access method, this problem causes handler::read_range_next to reject the current key when the upper bound key is a negative number because handler::compare_key incorrectly considers the positive and negative keys to be equal. The current patch does not correct the source of the problem in Field_longlong::cmp because it is not easy to propagate sign information about the constant at query execution time. Instead the patch changes the range optimizer so that it never compares unsiged fields with negative constants. As an added benefit, queries that do such comparisons will execute faster because the range optimizer replaces conditions like: (a) (unsigned_int [< | <=] negative_constant) == FALSE (b) (unsigned_int [> | >=] negative_constant) == TRUE with the corresponding constants. In some cases this may even result in constant time execution.
This commit is contained in:
@ -960,7 +960,9 @@ get_mm_parts(PARAM *param, COND *cond_func, Field *field,
|
||||
if (sel_arg->type == SEL_ARG::IMPOSSIBLE)
|
||||
{
|
||||
tree->type=SEL_TREE::IMPOSSIBLE;
|
||||
DBUG_RETURN(tree);
|
||||
/* If this is an NE_FUNC, we still need to check GT_FUNC. */
|
||||
if (!ne_func)
|
||||
DBUG_RETURN(tree);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -979,8 +981,9 @@ get_mm_parts(PARAM *param, COND *cond_func, Field *field,
|
||||
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);
|
||||
if (!tree2)
|
||||
DBUG_RETURN(0)
|
||||
tree= tree_or(param,tree,tree2);
|
||||
}
|
||||
DBUG_RETURN(tree);
|
||||
}
|
||||
@ -1159,6 +1162,35 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
|
||||
if (!(tree=new SEL_ARG(field,str,str2)))
|
||||
DBUG_RETURN(0); // out of memory
|
||||
|
||||
/*
|
||||
Check if we are comparing an UNSIGNED integer with a negative constant.
|
||||
In this case we know that:
|
||||
(a) (unsigned_int [< | <=] negative_constant) == FALSE
|
||||
(b) (unsigned_int [> | >=] negative_constant) == TRUE
|
||||
In case (a) the condition is false for all values, and in case (b) it
|
||||
is true for all values, so we can avoid unnecessary retrieval and condition
|
||||
testing, and we also get correct comparison of unsinged integers with
|
||||
negative integers (which otherwise fails because at query execution time
|
||||
negative integers are cast to unsigned if compared with unsigned).
|
||||
*/
|
||||
Item_result field_result_type= field->result_type();
|
||||
Item_result value_result_type= value->result_type();
|
||||
if (field_result_type == INT_RESULT && value_result_type == INT_RESULT &&
|
||||
((Field_num*)field)->unsigned_flag && !((Item_int*)value)->unsigned_flag)
|
||||
{
|
||||
longlong item_val= value->val_int();
|
||||
if (item_val < 0)
|
||||
{
|
||||
if (type == Item_func::LT_FUNC || type == Item_func::LE_FUNC)
|
||||
{
|
||||
tree->type= SEL_ARG::IMPOSSIBLE;
|
||||
DBUG_RETURN(tree);
|
||||
}
|
||||
if (type == Item_func::GT_FUNC || type == Item_func::GE_FUNC)
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Item_func::LT_FUNC:
|
||||
if (field_is_equal_to_item(field,value))
|
||||
|
Reference in New Issue
Block a user