Bug #4626 unsigned bigint / signed bigint
Submitted: 19 Jul 2004 14:58 Modified: 3 Sep 2004 16:09
Reporter: Torsten Bausch Email Updates:
Status: Won't fix Impact on me:
None 
Category:MySQL Server Severity:S1 (Critical)
Version:4.0.20 OS:
Assigned to: Antony Curtis CPU Architecture:Any

[19 Jul 2004 14:58] Torsten Bausch
Description:
I have found a possilble bug with the data type: 'unsigned bigint'.

I have created a easy table with:

CREATE TABLE `test` (
  `ID` bigint(20) unsigned NOT NULL default '0',
  `NAME` varchar(50) default NULL,
  PRIMARY KEY  (`ID`)
) TYPE=MyISAM; 

and added a few entries.

1. If you show all entries of the table then the result is correct.

mysql> SELECT * FROM test;
+----------------------+----------------------------------------------+
| ID                   | NAME                                         |
+----------------------+----------------------------------------------+
| 12345678901234567890 | ID greater LONGLONG_MAX (0x7FFFFFFFFFFFFFFF) |
|                    1 | Test                                         |
+----------------------+----------------------------------------------+
2 rows in set (0.00 sec)

2. If you add a condition like 'ID>10' then it's the result incorrect.

mysql> SELECT * FROM test WHERE ID>10;
Empty set (0.00 sec)

3. But if you expand the condition then the result is ok.

mysql> SELECT * FROM test WHERE (ID>10 OR ID<0);
+----------------------+----------------------------------------------+
| ID                   | NAME                                         |
+----------------------+----------------------------------------------+
| 12345678901234567890 | ID greater LONGLONG_MAX (0x7FFFFFFFFFFFFFFF) |
+----------------------+----------------------------------------------+
1 row in set (0.01 sec)

4. 

mysql> SELECT * FROM test WHERE ID<0;
Empty set (0.00 sec)

How to repeat:
see Description
[20 Jul 2004 13:36] Aleksey Kishkin
verified on 4.0.20-standard
[12 Aug 2004 0:27] Antony Curtis
Problems found where comparing signed and unsigned types, 
 
===== mysql-test/t/bigint.test 1.20 vs edited ===== 
--- 1.20/mysql-test/t/bigint.test	2004-02-04 08:59:14 +00:00 
+++ edited/mysql-test/t/bigint.test	2004-08-11 22:36:48 +01:00 
@@ -63,3 +63,13 @@ 
 # atof() behaviour is different of different systems. to be fixed in 4.1 
 #SELECT '0x8000000000000001'+0; 
  
+# 
+# Unsigned bigint / signed bigint comparison failure 
+# Bug#4626 
+# 
+ 
+CREATE TABLE t1 ( `ID` bigint(20) unsigned NOT NULL default '0', `NAME` 
varchar(50) default NULL, PRIMARY KEY (`ID`) ) ENGINE=MYISAM; 
+insert into t1 values (12345678901234567890,'ID greater LONGLONG_MAX 
(0x7FFFFFFFFFFFFFFF)'), (1,'Test'); 
+select * from t1 where `ID` > 10; 
+select * from t1 where `ID` < 0; 
+drop table t1; 
===== mysql-test/r/bigint.result 1.21 vs edited ===== 
--- 1.21/mysql-test/r/bigint.result	2004-02-04 08:59:14 +00:00 
+++ edited/mysql-test/r/bigint.result	2004-08-11 22:37:11 +01:00 
@@ -81,3 +81,11 @@ 
 10000000000000000000 
 10000000000000000000 
 drop table t1; 
+CREATE TABLE t1 ( `ID` bigint(20) unsigned NOT NULL default '0', `NAME` 
varchar(50) default NULL, PRIMARY KEY (`ID`) ) ENGINE=MYISAM; 
+insert into t1 values (12345678901234567890,'ID greater LONGLONG_MAX 
(0x7FFFFFFFFFFFFFFF)'), (1,'Test'); 
+select * from t1 where `ID` > 10; 
+ID	NAME 
+12345678901234567890	ID greater LONGLONG_MAX (0x7FFFFFFFFFFFFFFF) 
+select * from t1 where `ID` < 0; 
+ID	NAME 
+drop table t1; 
===== sql/item.h 1.44 vs edited ===== 
--- 1.44/sql/item.h	2004-04-28 01:37:44 +01:00 
+++ edited/sql/item.h	2004-08-11 22:42:15 +01:00 
@@ -21,6 +21,7 @@ 
  
 struct st_table_list; 
 void item_init(void);				/* Init item functions */ 
+int lex_ulonglong(const char *str,uint length); 
  
 class Item { 
   Item(const Item &);				/* Prevent use of these */ 
@@ -225,7 +226,10 @@ 
 public: 
   Item_uint(const char *str_arg, uint length) : 
     Item_int(str_arg, (longlong) strtoull(str_arg,(char**) 0,10), length) {} 
-  Item_uint(uint32 i) :Item_int((longlong) i, 10) {} 
+  Item_uint(uint32 i, uint length=10) :Item_int((longlong) i, length) {} 
+#ifdef HAVE_LONG_LONG   
+  Item_uint(ulonglong i, uint length=20) :Item_int((longlong) i, length) {} 
+#endif   
   double val() { return ulonglong2double(value); } 
   String *val_str(String*); 
   void make_field(Send_field *field); 
@@ -291,6 +295,7 @@ 
     max_length=length; 
     name=(char*) str_value.ptr(); 
     decimals=NOT_FIXED_DEC; 
+    unsigned_flag= lex_ulonglong(str_value.ptr(), str_value.length()); 
   } 
   Item_string(const char *name_par,const char *str,uint length) 
   { 
@@ -298,11 +303,12 @@ 
     max_length=length; 
     name=(char*) name_par; 
     decimals=NOT_FIXED_DEC; 
+    unsigned_flag= lex_ulonglong(str_value.ptr(), str_value.length()); 
   } 
   ~Item_string() {} 
   enum Type type() const { return STRING_ITEM; } 
   double val() { return atof(str_value.ptr()); } 
-  longlong val_int() { return strtoll(str_value.ptr(),(char**) 0,10); } 
+  longlong val_int() { return unsigned_flag ? strtoll(str_value.ptr(),(char**) 
0,10) : (longlong)strtoull(str_value.ptr(),(char**) 0,10); } 
   String *val_str(String*) { return (String*) &str_value; } 
   bool save_in_field(Field *field, bool no_conversions); 
   void make_field(Send_field *field); 
@@ -311,7 +317,7 @@ 
   bool eq(const Item *item, bool binary_cmp) const; 
   Item *new_item() { return new 
Item_string(name,str_value.ptr(),max_length); } 
   String *const_string() { return &str_value; } 
-  inline void append(char *str,uint length) { str_value.append(str,length); } 
+  inline void append(char *str,uint length) { str_value.append(str,length); 
unsigned_flag= lex_ulonglong(str_value.ptr(), str_value.length()); } 
   void print(String *str); 
   unsigned int size_of() { return sizeof(*this);} 
 }; 
@@ -462,6 +468,19 @@ 
   Item *ref; 
 public: 
   Item_int_with_ref(longlong i, Item *ref_arg) :Item_int(i), ref(ref_arg) 
+  {} 
+  bool save_in_field(Field *field, bool no_conversions) 
+  { 
+    return ref->save_in_field(field, no_conversions); 
+  } 
+  unsigned int size_of() { return sizeof(*this);}   
+}; 
+ 
+class Item_uint_with_ref :public Item_uint 
+{ 
+  Item *ref; 
+public: 
+  Item_uint_with_ref(ulonglong i, Item *ref_arg) :Item_uint(i), ref(ref_arg) 
   {} 
   bool save_in_field(Field *field, bool no_conversions) 
   { 
===== sql/item_cmpfunc.cc 1.62 vs edited ===== 
--- 1.62/sql/item_cmpfunc.cc	2004-06-07 11:38:32 +01:00 
+++ edited/sql/item_cmpfunc.cc	2004-08-11 22:46:36 +01:00 
@@ -51,8 +51,15 @@ 
     if (!(*item)->save_in_field(field, 1) && 
 	!((*item)->null_value)) 
     { 
-      Item *tmp=new Item_int_with_ref(field->val_int(), *item); 
-      if (tmp) 
+      Item *tmp; 
+      if ((*item)->unsigned_flag) 
+      { 
+      	tmp= new Item_uint_with_ref((ulonglong)field->val_int(), *item); 
+	tmp->unsigned_flag= 1; 
+      } 
+      else 
+      	tmp= new Item_int_with_ref(field->val_int(), *item); 
+      if (tmp)  
 	*item=tmp; 
       return 1;					// Item was replaced 
     } 
@@ -159,8 +166,17 @@ 
     if (!args[1]->null_value) 
     { 
       null_value=0; 
-      if (val1 < val2)	return -1; 
+      longlong x1=val1, x2=val2; 
+ 
+      if (args[0]->unsigned_flag) *((ulonglong *)&x1) >>= 1; else x1 >>= 1; 
+      if (args[1]->unsigned_flag) *((ulonglong *)&x2) >>= 1; else x2 >>= 1; 
+       
+      if (x1 < x2) return -1; 
+      if (x2 < x1) return 1; 
+ 
       if (val1 == val2)   return 0; 
+       
+      if (val1 < val2)	return -1; 
       return 1; 
     } 
   } 
===== sql/set_var.cc 1.57 vs edited ===== 
--- 1.57/sql/set_var.cc	2004-06-01 15:29:22 +01:00 
+++ edited/sql/set_var.cc	2004-08-11 17:26:14 +01:00 
@@ -1093,7 +1093,7 @@ 
   } 
   switch (type()) { 
   case SHOW_LONG: 
-    return new Item_uint((int32) *(ulong*) value_ptr(thd, var_type)); 
+    return new Item_uint((uint32) *(ulong*) value_ptr(thd, var_type)); 
   case SHOW_LONGLONG: 
   { 
     longlong value; 
===== sql/sql_lex.cc 1.40 vs edited ===== 
--- 1.40/sql/sql_lex.cc	2003-07-02 16:55:09 +01:00 
+++ edited/sql/sql_lex.cc	2004-08-11 18:20:52 +01:00 
@@ -414,6 +414,20 @@ 
   return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger; 
 } 
  
+int lex_ulonglong(const char *str,uint length) 
+{ 
+  int i = 0; 
+  if ((*str == '+') || (*str == '-')) 
+    i++; 
+  for (; i < length; i++) { 
+    if ((str[i] < '0') || (str[i] > '9'))  
+    { 
+      length = i; 
+      break; 
+    } 
+  } 
+  return int_token(str,length) == ULONGLONG_NUM; 
+} 
  
 // yylex remember the following states from the following yylex() 
 // STATE_EOQ ; found end of query
[3 Sep 2004 16:09] Sergei Golubchik
This patch is unfortunately too big and intrusive for the stable version 4.0
We fixed this bug in 4.1 instead, try 4.1.4-gamma