Commit 1a8c334b authored by unknown's avatar unknown

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.


mysql-test/r/range.result:
  - Added test for BUG#11185
  - Added missing test from 4.1. This test also tests the fix for BUG#11185.
mysql-test/t/range.test:
  - Added test for BUG#11185
  - Added missing test from 4.1. This test also tests the fix for BUG#11185.
sql/opt_range.cc:
  Added a new optimization to the range optimizer where we detect that
  an UNSIGNED field is compared with a negative constant. Depending on
  the comparison operator, we know directly that the result of the
  comparison is either TRUE or FALSE for all input values, and we need
  not check each value.
      
  This optimization is also necessary so that the index range access
  method produces correct results when comparing unsigned fields with
  negative constants.
parents 902376e5 1abe8e69
...@@ -544,11 +544,71 @@ count(*) ...@@ -544,11 +544,71 @@ count(*)
1 1
select count(*) from t2 where x > -16; select count(*) from t2 where x > -16;
count(*) count(*)
1 2
select count(*) from t2 where x = 18446744073709551601; select count(*) from t2 where x = 18446744073709551601;
count(*) count(*)
0 0
drop table t1,t2; drop table t1,t2;
create table t1 (x bigint unsigned not null primary key) engine=innodb;
insert into t1(x) values (0xfffffffffffffff0);
insert into t1(x) values (0xfffffffffffffff1);
select * from t1;
x
18446744073709551600
18446744073709551601
select count(*) from t1 where x>0;
count(*)
2
select count(*) from t1 where x=0;
count(*)
0
select count(*) from t1 where x<0;
count(*)
0
select count(*) from t1 where x < -16;
count(*)
0
select count(*) from t1 where x = -16;
count(*)
0
select count(*) from t1 where x > -16;
count(*)
2
select count(*) from t1 where x = 18446744073709551601;
count(*)
1
drop table t1;
create table t1 (a bigint unsigned);
create index t1i on t1(a);
insert into t1 select 18446744073709551615;
insert into t1 select 18446744073709551614;
explain select * from t1 where a <> -1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index t1i t1i 9 NULL 2 Using where; Using index
select * from t1 where a <> -1;
a
18446744073709551614
18446744073709551615
explain select * from t1 where a > -1 or a < -1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index t1i t1i 9 NULL 2 Using where; Using index
select * from t1 where a > -1 or a < -1;
a
18446744073709551614
18446744073709551615
explain select * from t1 where a > -1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index t1i t1i 9 NULL 2 Using where; Using index
select * from t1 where a > -1;
a
18446744073709551614
18446744073709551615
explain select * from t1 where a < -1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
select * from t1 where a < -1;
a
drop table t1;
set names latin1; set names latin1;
create table t1 (a char(10), b text, key (a)) character set latin1; create table t1 (a char(10), b text, key (a)) character set latin1;
INSERT INTO t1 (a) VALUES INSERT INTO t1 (a) VALUES
......
...@@ -418,6 +418,41 @@ select count(*) from t2 where x > -16; ...@@ -418,6 +418,41 @@ select count(*) from t2 where x > -16;
select count(*) from t2 where x = 18446744073709551601; select count(*) from t2 where x = 18446744073709551601;
drop table t1,t2; drop table t1,t2;
--disable_warnings
create table t1 (x bigint unsigned not null primary key) engine=innodb;
--enable_warnings
insert into t1(x) values (0xfffffffffffffff0);
insert into t1(x) values (0xfffffffffffffff1);
select * from t1;
select count(*) from t1 where x>0;
select count(*) from t1 where x=0;
select count(*) from t1 where x<0;
select count(*) from t1 where x < -16;
select count(*) from t1 where x = -16;
select count(*) from t1 where x > -16;
select count(*) from t1 where x = 18446744073709551601;
drop table t1;
#
# Bug #11185 incorrect comparison of unsigned int to signed constant
#
create table t1 (a bigint unsigned);
create index t1i on t1(a);
insert into t1 select 18446744073709551615;
insert into t1 select 18446744073709551614;
explain select * from t1 where a <> -1;
select * from t1 where a <> -1;
explain select * from t1 where a > -1 or a < -1;
select * from t1 where a > -1 or a < -1;
explain select * from t1 where a > -1;
select * from t1 where a > -1;
explain select * from t1 where a < -1;
select * from t1 where a < -1;
drop table t1;
# #
# Bug #6045: Binary Comparison regression in MySQL 4.1 # Bug #6045: Binary Comparison regression in MySQL 4.1
# Binary searches didn't use a case insensitive index. # Binary searches didn't use a case insensitive index.
......
...@@ -3840,6 +3840,35 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, ...@@ -3840,6 +3840,35 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
if (!(tree=new SEL_ARG(field,str,str))) if (!(tree=new SEL_ARG(field,str,str)))
DBUG_RETURN(0); // out of memory 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) { switch (type) {
case Item_func::LT_FUNC: case Item_func::LT_FUNC:
if (field_is_equal_to_item(field,value)) if (field_is_equal_to_item(field,value))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment