From e42f4e31990d7741e7c375f1102c4584a507c05f Mon Sep 17 00:00:00 2001
From: Alexander Barkov <bar@mariadb.org>
Date: Wed, 3 Sep 2014 16:31:47 +0400
Subject: [PATCH] MDEV-6688 Illegal mix of collation with bit string
 B'01100001'

---
 mysql-test/r/ctype_utf8.result | 12 +++++
 mysql-test/t/ctype_utf8.test   | 10 +++++
 sql/item.h                     | 52 ++++++++++++++--------
 sql/sql_yacc.yy                | 80 ++++++----------------------------
 4 files changed, 71 insertions(+), 83 deletions(-)

diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result
index d768550e2c6..8a3fcd9dc0d 100644
--- a/mysql-test/r/ctype_utf8.result
+++ b/mysql-test/r/ctype_utf8.result
@@ -5996,5 +5996,17 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 DEALLOCATE PREPARE stmt;
 DROP TABLE t1;
 #
+# MDEV-6688 Illegal mix of collation with bit string B'01100001'
+#
+CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1, b INT);
+INSERT INTO t1 VALUES ('a',1);
+SELECT CONCAT(a, IF(b>10, _utf8 X'61', _utf8 X'61')) FROM t1;
+CONCAT(a, IF(b>10, _utf8 X'61', _utf8 X'61'))
+aa
+SELECT CONCAT(a, IF(b>10, _utf8 X'61', _utf8 B'01100001')) FROM t1;
+CONCAT(a, IF(b>10, _utf8 X'61', _utf8 B'01100001'))
+aa
+DROP TABLE t1;
+#
 # End of 10.0 tests
 #
diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test
index 0ccdfe8aacf..75630cf9cd5 100644
--- a/mysql-test/t/ctype_utf8.test
+++ b/mysql-test/t/ctype_utf8.test
@@ -1710,6 +1710,16 @@ EXECUTE stmt USING @arg;
 DEALLOCATE PREPARE stmt;
 DROP TABLE t1;
 
+--echo #
+--echo # MDEV-6688 Illegal mix of collation with bit string B'01100001'
+--echo #
+CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1, b INT);
+INSERT INTO t1 VALUES ('a',1);
+SELECT CONCAT(a, IF(b>10, _utf8 X'61', _utf8 X'61')) FROM t1;
+SELECT CONCAT(a, IF(b>10, _utf8 X'61', _utf8 B'01100001')) FROM t1;
+DROP TABLE t1;
+
+
 --echo #
 --echo # End of 10.0 tests
 --echo #
diff --git a/sql/item.h b/sql/item.h
index 3d60794061b..814ec121803 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2711,6 +2711,23 @@ class Item_static_float_func :public Item_float
 
 class Item_string :public Item_basic_constant
 {
+  bool m_cs_specified;
+protected:
+  /**
+    Set the value of m_cs_specified attribute.
+
+    m_cs_specified attribute shows whether character-set-introducer was
+    explicitly specified in the original query for this text literal or
+    not. The attribute makes sense (is used) only for views.
+
+    This operation is to be called from the parser during parsing an input
+    query.
+  */
+  inline void set_cs_specified(bool cs_specified)
+  {
+    m_cs_specified= cs_specified;
+  }
+
 public:
   Item_string(const char *str,uint length,
               CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
@@ -2860,21 +2877,6 @@ class Item_string :public Item_basic_constant
     return m_cs_specified;
   }
 
-  /**
-    Set the value of m_cs_specified attribute.
-
-    m_cs_specified attribute shows whether character-set-introducer was
-    explicitly specified in the original query for this text literal or
-    not. The attribute makes sense (is used) only for views.
-
-    This operation is to be called from the parser during parsing an input
-    query.
-  */
-  inline void set_cs_specified(bool cs_specified)
-  {
-    m_cs_specified= cs_specified;
-  }
-
   String *check_well_formed_result(bool send_error)
   { return Item::check_well_formed_result(&str_value, send_error); }
 
@@ -2906,8 +2908,24 @@ class Item_string :public Item_basic_constant
     }
     return MYSQL_TYPE_STRING; // Not a temporal literal
   }
-private:
-  bool m_cs_specified;
+};
+
+
+class Item_string_with_introducer :public Item_string
+{
+public:
+  Item_string_with_introducer(const char *str, uint length, CHARSET_INFO *cs)
+    :Item_string(str, length, cs)
+  {
+     set_repertoire_from_value();
+     set_cs_specified(true);
+  }
+  Item_string_with_introducer(const String *str, CHARSET_INFO *tocs)
+    :Item_string(str->ptr(), str->length(), tocs)
+  {
+     set_repertoire_from_value();
+     set_cs_specified(true);
+  }
 };
 
 
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ef71369ac66..ecef9d8841a 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1648,7 +1648,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
 
 %type <lex_str>
         IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
-        HEX_NUM HEX_STRING hex_num_or_string
+        HEX_NUM HEX_STRING
         LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
         IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
         NCHAR_STRING opt_component key_cache_name
@@ -1666,7 +1666,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
         remember_name remember_end opt_db text_or_password
 
 %type <string>
-        text_string opt_gconcat_separator
+        text_string hex_or_bin_String opt_gconcat_separator
 
 %type <num>
         type type_with_opt_collate int_type real_type order_dir lock_option
@@ -6517,11 +6517,6 @@ now_or_signed_literal:
           { $$=$1; }
         ;
 
-hex_num_or_string:
-          HEX_NUM {}
-        | HEX_STRING {}
-        ;
-
 charset:
           CHAR_SYM SET {}
         | CHARSET {}
@@ -13253,14 +13248,10 @@ text_literal:
           }
         | UNDERSCORE_CHARSET TEXT_STRING
           {
-            Item_string *str= new (thd->mem_root) Item_string($2.str,
+            $$= new (thd->mem_root) Item_string_with_introducer($2.str,
                                                                 $2.length, $1);
-            if (str == NULL)
+            if ($$ == NULL)
               MYSQL_YYABORT;
-            str->set_repertoire_from_value();
-            str->set_cs_specified(TRUE);
-
-            $$= str;
           }
         | text_literal TEXT_STRING_literal
           {
@@ -13289,7 +13280,12 @@ text_string:
             if ($$ == NULL)
               MYSQL_YYABORT;
           }
-        | HEX_NUM
+          | hex_or_bin_String { $$= $1; }
+          ;
+
+
+hex_or_bin_String:
+          HEX_NUM
           {
             Item *tmp= new (thd->mem_root) Item_hex_hybrid($1.str, $1.length);
             if (tmp == NULL)
@@ -13394,60 +13390,12 @@ literal:
             if ($$ == NULL)
               MYSQL_YYABORT;
           }
-        | UNDERSCORE_CHARSET hex_num_or_string
+        | UNDERSCORE_CHARSET hex_or_bin_String
           {
-            Item *tmp= new (thd->mem_root) Item_hex_string($2.str, $2.length);
-            if (tmp == NULL)
+            Item_string_with_introducer *item_str;
+            item_str= new (thd->mem_root) Item_string_with_introducer($2, $1);
+            if (!item_str || !item_str->check_well_formed_result(true))
               MYSQL_YYABORT;
-            /*
-              it is OK only emulate fix_fieds, because we need only
-              value of constant
-            */
-            tmp->quick_fix_field();
-            String *str= tmp->val_str((String*) 0);
-
-            Item_string *item_str;
-            item_str= new (thd->mem_root)
-                        Item_string(NULL, /* name will be set in select_item */
-                                    str ? str->ptr() : "",
-                                    str ? str->length() : 0,
-                                    $1);
-            if (!item_str ||
-                !item_str->check_well_formed_result(true))
-            {
-              MYSQL_YYABORT;
-            }
-
-            item_str->set_repertoire_from_value();
-            item_str->set_cs_specified(TRUE);
-
-            $$= item_str;
-          }
-        | UNDERSCORE_CHARSET BIN_NUM
-          {
-            Item *tmp= new (thd->mem_root) Item_bin_string($2.str, $2.length);
-            if (tmp == NULL)
-              MYSQL_YYABORT;
-            /*
-              it is OK only emulate fix_fieds, because we need only
-              value of constant
-            */
-            tmp->quick_fix_field();
-            String *str= tmp->val_str((String*) 0);
-
-            Item_string *item_str;
-            item_str= new (thd->mem_root)
-                        Item_string(NULL, /* name will be set in select_item */
-                                    str ? str->ptr() : "",
-                                    str ? str->length() : 0,
-                                    $1);
-            if (!item_str ||
-                !item_str->check_well_formed_result(true))
-            {
-              MYSQL_YYABORT;
-            }
-
-            item_str->set_cs_specified(TRUE);
 
             $$= item_str;
           }
-- 
2.30.9