List:Commits« Previous MessageNext Message »
From:Jorgen Loland Date:February 16 2011 1:42pm
Subject:bzr commit into mysql-trunk branch (jorgen.loland:3655) Bug#11766317
View as plain text  
#At file:///export/home/jl208045/mysql/mysql-trunk-59405/ based on revid:tor.didriksen@stripped

 3655 Jorgen Loland	2011-02-16
      BUG#11766317: FIND_IN_SET won't work normaly after upgrade 
                    from 5.1 to 5.5
      
      (Former 59405)
      
      In this bug, args[0] in an Item_func_find_in_set stored an 
      Item_func_weekday that was constant. In 
      Item_func_find_in_set::fix_length_and_dec(), args[0]->val_str() 
      was called. Later, when Item_func_find_in_set::val_int() was 
      called, args[0]->null_value was checked. However, the 
      Item_func_weekday in args[0] had now been replaced with an 
      Item_cache. No val_*() calls had been made to this Item_cache,
      thus null_value was incorrectly 'true', resulting in missing 
      rows in the result set.
      
      The implemented fix is to remember args[0]->null_value as it 
      was right after calling it's val_str() in fix_length_and_dec().
      Note: this only applies if args[0] is constant. 
      
      An alternative fix would be to call args[0]->val_int() inside 
      Item_func_find_in_set::val_int(), but that would have to be 
      done for every record this const value is checked against.
     @ mysql-test/r/func_set.result
        Add test for BUG#59405
     @ mysql-test/t/func_set.test
        Add test for BUG#59405
     @ sql/item_func.cc
        Add variable Item_func_find_in_set::args_0_null_value
     @ sql/item_func.h
        Add variable Item_func_find_in_set::args_0_null_value

    modified:
      mysql-test/r/func_set.result
      mysql-test/t/func_set.test
      sql/item_func.cc
      sql/item_func.h
=== modified file 'mysql-test/r/func_set.result'
--- a/mysql-test/r/func_set.result	2009-06-16 14:36:15 +0000
+++ b/mysql-test/r/func_set.result	2011-02-16 13:42:54 +0000
@@ -159,3 +159,45 @@ SELECT CONVERT( a USING latin1 ) FROM t2
 CONVERT( a USING latin1 )
 
 DROP TABLE t1, t2;
+#
+# BUG#59405: FIND_IN_SET won't work normaly after upgrade from 5.1 to 5.5
+#
+CREATE TABLE t1(days set('1','2','3','4','5','6','7'));
+INSERT INTO t1 VALUES('1,2,3,4,5,6,7'), (NULL), ('1,2,3,4,5,6,7');
+
+SELECT * FROM t1 WHERE FIND_IN_SET(DAYOFWEEK(CURRENT_DATE()), days);
+days
+1,2,3,4,5,6,7
+1,2,3,4,5,6,7
+SELECT * FROM t1 WHERE FIND_IN_SET(DAYOFWEEK(CURRENT_DATE()), days) IS UNKNOWN;
+days
+NULL
+SELECT * FROM t1 WHERE FIND_IN_SET(DAYOFWEEK(CURRENT_DATE()), NULL);
+days
+SELECT * FROM t1 WHERE FIND_IN_SET(DAYOFWEEK(CURRENT_DATE()), NULL) IS UNKNOWN;
+days
+1,2,3,4,5,6,7
+NULL
+1,2,3,4,5,6,7
+SELECT * FROM t1 WHERE FIND_IN_SET(7, days);
+days
+1,2,3,4,5,6,7
+1,2,3,4,5,6,7
+SELECT * FROM t1 WHERE FIND_IN_SET(8, days);
+days
+SELECT * FROM t1 WHERE FIND_IN_SET(NULL, days);
+days
+SELECT * FROM t1 WHERE FIND_IN_SET(NULL, days) IS UNKNOWN;
+days
+1,2,3,4,5,6,7
+NULL
+1,2,3,4,5,6,7
+SELECT * FROM t1 WHERE FIND_IN_SET(NULL, NULL);
+days
+SELECT * FROM t1 WHERE FIND_IN_SET(NULL, NULL) IS UNKNOWN;
+days
+1,2,3,4,5,6,7
+NULL
+1,2,3,4,5,6,7
+
+DROP TABLE t1;

=== modified file 'mysql-test/t/func_set.test'
--- a/mysql-test/t/func_set.test	2009-06-16 14:36:15 +0000
+++ b/mysql-test/t/func_set.test	2011-02-16 13:42:54 +0000
@@ -97,3 +97,25 @@ SELECT CONVERT( a USING latin1 ) FROM t1
 SELECT CONVERT( a USING latin1 ) FROM t2;
 
 DROP TABLE t1, t2;
+
+--echo #
+--echo # BUG#59405: FIND_IN_SET won't work normaly after upgrade from 5.1 to 5.5
+--echo #
+
+CREATE TABLE t1(days set('1','2','3','4','5','6','7'));
+INSERT INTO t1 VALUES('1,2,3,4,5,6,7'), (NULL), ('1,2,3,4,5,6,7');
+
+--echo
+SELECT * FROM t1 WHERE FIND_IN_SET(DAYOFWEEK(CURRENT_DATE()), days);
+SELECT * FROM t1 WHERE FIND_IN_SET(DAYOFWEEK(CURRENT_DATE()), days) IS UNKNOWN; 
+SELECT * FROM t1 WHERE FIND_IN_SET(DAYOFWEEK(CURRENT_DATE()), NULL);
+SELECT * FROM t1 WHERE FIND_IN_SET(DAYOFWEEK(CURRENT_DATE()), NULL) IS UNKNOWN; 
+SELECT * FROM t1 WHERE FIND_IN_SET(7, days);
+SELECT * FROM t1 WHERE FIND_IN_SET(8, days);
+SELECT * FROM t1 WHERE FIND_IN_SET(NULL, days); 
+SELECT * FROM t1 WHERE FIND_IN_SET(NULL, days) IS UNKNOWN; 
+SELECT * FROM t1 WHERE FIND_IN_SET(NULL, NULL); 
+SELECT * FROM t1 WHERE FIND_IN_SET(NULL, NULL) IS UNKNOWN; 
+
+--echo
+DROP TABLE t1;

=== modified file 'sql/item_func.cc'
--- a/sql/item_func.cc	2011-02-08 15:54:12 +0000
+++ b/sql/item_func.cc	2011-02-16 13:42:54 +0000
@@ -3019,6 +3019,12 @@ void Item_func_find_in_set::fix_length_a
     if (field->real_type() == MYSQL_TYPE_SET)
     {
       String *find=args[0]->val_str(&value);
+      /* 
+        Because it is constant, args[0] may be replaced by an Item_cache.
+        Store null_value here to avoid args[0]->val_int() before checking
+        the null_value in Item_func_find_in_set::val_int().
+       */
+      args0_null_value= args[0]->null_value; 
       if (find)
       {
 	enum_value= find_type(((Field_enum*) field)->typelib,find->ptr(),
@@ -3039,11 +3045,20 @@ longlong Item_func_find_in_set::val_int(
   DBUG_ASSERT(fixed == 1);
   if (enum_value)
   {
-    ulonglong tmp=(ulonglong) args[1]->val_int();
-    if (!(null_value=args[1]->null_value || args[0]->null_value))
+    // enum_value is set iff args[0]->const_item() in fix_length_and_dec().
+    DBUG_ASSERT(args[0]->const_item());
+
+    if (args0_null_value)
+      null_value= true;
+    else
     {
-      if (tmp & enum_bit)
-	return enum_value;
+      ulonglong tmp= (ulonglong) args[1]->val_int();
+      null_value= args[1]->null_value;
+      if (!null_value)
+      {
+        if (tmp & enum_bit)
+          return enum_value;
+      }
     }
     return 0L;
   }

=== modified file 'sql/item_func.h'
--- a/sql/item_func.h	2011-02-08 15:54:12 +0000
+++ b/sql/item_func.h	2011-02-16 13:42:54 +0000
@@ -1025,9 +1025,11 @@ class Item_func_find_in_set :public Item
   String value,value2;
   uint enum_value;
   ulonglong enum_bit;
+  bool args0_null_value;  /* Cache args[0]->null_value iff args[0] is const */
   DTCollation cmp_collation;
 public:
-  Item_func_find_in_set(Item *a,Item *b) :Item_int_func(a,b),enum_value(0) {}
+  Item_func_find_in_set(Item *a,Item *b) 
+    : Item_int_func(a,b), enum_value(0), args0_null_value(false) {}
   longlong val_int();
   const char *func_name() const { return "find_in_set"; }
   void fix_length_and_dec();


Attachment: [text/bzr-bundle] bzr/jorgen.loland@oracle.com-20110216134254-kxkel6d8bd113l6d.bundle
Thread
bzr commit into mysql-trunk branch (jorgen.loland:3655) Bug#11766317Jorgen Loland16 Feb