diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 6df76da91d8..4ca96316800 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -475,3 +475,89 @@ id name uid id name uid 1025 Y 25 1025 Y 25 1026 Z 26 1026 Z 26 drop table t1,t2; +create table t1 (x bigint unsigned not null); +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 +create table t2 (x bigint not null); +insert into t2(x) values (0xfffffffffffffff0); +insert into t2(x) values (0xfffffffffffffff1); +select * from t2; +x +-16 +-15 +select count(*) from t2 where x>0; +count(*) +0 +select count(*) from t2 where x=0; +count(*) +0 +select count(*) from t2 where x<0; +count(*) +2 +select count(*) from t2 where x < -16; +count(*) +0 +select count(*) from t2 where x = -16; +count(*) +1 +select count(*) from t2 where x > -16; +count(*) +1 +select count(*) from t2 where x = 18446744073709551601; +count(*) +0 +drop table t1; +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(*) +1 +select count(*) from t1 where x = 18446744073709551601; +count(*) +1 +drop table t1; diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 471af8e4a5b..6037f68db55 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -383,3 +383,46 @@ select * from t1, t2 where t1.uid=t2.uid AND t1.uid > 0; select * from t1, t2 where t1.uid=t2.uid AND t1.uid != 0; drop table t1,t2; + +# Fix for bug#4488 +# +create table t1 (x bigint unsigned not null); +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; + + +create table t2 (x bigint not null); +insert into t2(x) values (0xfffffffffffffff0); +insert into t2(x) values (0xfffffffffffffff1); +select * from t2; +select count(*) from t2 where x>0; +select count(*) from t2 where x=0; +select count(*) from t2 where x<0; +select count(*) from t2 where x < -16; +select count(*) from t2 where x = -16; +select count(*) from t2 where x > -16; +select count(*) from t2 where x = 18446744073709551601; + +drop table t1; +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; +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; + diff --git a/sql/item.h b/sql/item.h index e1bed6bd1d8..6900fa11b90 100644 --- a/sql/item.h +++ b/sql/item.h @@ -862,7 +862,7 @@ public: }; /* - The following class is used to optimize comparing of date columns + The following class is used to optimize comparing of date and bigint columns We need to save the original item, to be able to set the field to the original value in 'opt_range'. */ @@ -872,7 +872,9 @@ class Item_int_with_ref :public Item_int Item *ref; public: Item_int_with_ref(longlong i, Item *ref_arg) :Item_int(i), ref(ref_arg) - {} + { + unsigned_flag= ref_arg->unsigned_flag; + } int save_in_field(Field *field, bool no_conversions) { return ref->save_in_field(field, no_conversions); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 14c0d996360..45a1918de1c 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -315,6 +315,22 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) func= &Arg_comparator::compare_e_binary_string; } } + else if (type == INT_RESULT) + { + if (func == &Arg_comparator::compare_int) + { + if ((*a)->unsigned_flag) + func= ((*b)->unsigned_flag)? &Arg_comparator::compare_int_unsigned : + &Arg_comparator::compare_int_unsigned_signed; + else if ((*b)->unsigned_flag) + func= &Arg_comparator::compare_int_signed_unsigned; + } + else if (func== &Arg_comparator::compare_e_int) + { + if ((*a)->unsigned_flag ^ (*b)->unsigned_flag) + func= &Arg_comparator::compare_e_int_diff_signedness; + } + } return 0; } @@ -434,6 +450,82 @@ int Arg_comparator::compare_int() return -1; } + +/* + Compare values as BIGINT UNSIGNED. +*/ + +int Arg_comparator::compare_int_unsigned() +{ + ulonglong val1= (*a)->val_int(); + if (!(*a)->null_value) + { + ulonglong val2= (*b)->val_int(); + if (!(*b)->null_value) + { + owner->null_value= 0; + if (val1 < val2) return -1; + if (val1 == val2) return 0; + return 1; + } + } + owner->null_value= 1; + return -1; +} + + +/* + Compare signed (*a) with unsigned (*B) +*/ + +int Arg_comparator::compare_int_signed_unsigned() +{ + longlong sval1= (*a)->val_int(); + if (!(*a)->null_value) + { + ulonglong uval2= (ulonglong)(*b)->val_int(); + if (!(*b)->null_value) + { + owner->null_value= 0; + if (sval1 < 0 || (ulonglong)sval1 < uval2) + return -1; + if ((ulonglong)sval1 == uval2) + return 0; + return 1; + } + } + owner->null_value= 1; + return -1; +} + + +/* + Compare unsigned (*a) with signed (*B) +*/ + +int Arg_comparator::compare_int_unsigned_signed() +{ + ulonglong uval1= (ulonglong)(*a)->val_int(); + if (!(*a)->null_value) + { + longlong sval2= (*b)->val_int(); + if (!(*b)->null_value) + { + owner->null_value= 0; + if (sval2 < 0) + return 1; + if (uval1 < (ulonglong)sval2) + return -1; + if (uval1 == (ulonglong)sval2) + return 0; + return 1; + } + } + owner->null_value= 1; + return -1; +} + + int Arg_comparator::compare_e_int() { longlong val1= (*a)->val_int(); @@ -443,6 +535,17 @@ int Arg_comparator::compare_e_int() return test(val1 == val2); } +/* + Compare unsigned *a with signed *b or signed *a with unsigned *b. +*/ +int Arg_comparator::compare_e_int_diff_signedness() +{ + longlong val1= (*a)->val_int(); + longlong val2= (*b)->val_int(); + if ((*a)->null_value || (*b)->null_value) + return test((*a)->null_value && (*b)->null_value); + return (val1 >= 0) && test(val1 == val2); +} int Arg_comparator::compare_row() { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 7c96226b08a..415fae59de9 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -66,11 +66,15 @@ public: int compare_binary_string(); // compare args[0] & args[1] int compare_real(); // compare args[0] & args[1] int compare_int(); // compare args[0] & args[1] + int compare_int_signed_unsigned(); + int compare_int_unsigned_signed(); + int compare_int_unsigned(); int compare_row(); // compare args[0] & args[1] int compare_e_string(); // compare args[0] & args[1] int compare_e_binary_string(); // compare args[0] & args[1] int compare_e_real(); // compare args[0] & args[1] int compare_e_int(); // compare args[0] & args[1] + int compare_e_int_diff_signedness(); int compare_e_row(); // compare args[0] & args[1] static arg_cmp_func comparator_matrix [4][2];