Repository: blackbeam/rust-mysql-simple (language: rust)

Issue:
4-byte utf-8 codepoints not supported
The `utf8` charset encoding in MySQL is broken (it only supports up to 3-byte codepoints) and the "fixed" charset is called `utf8mb4`. By default this library seems to use `utf8`, which results in not being able to insert some characters into the database



Discussion / hints:

Fix patch:
diff --git a/src/conn/mod.rs b/src/conn/mod.rs
index e3fa1a3f..cc487f2a 100644
--- a/src/conn/mod.rs
+++ b/src/conn/mod.rs
@@ -1121,7 +1121,7 @@ impl Conn {
                             if !handshake.capability_flags.contains(consts::CLIENT_SSL) {
                                 return Err(DriverError(SslNotSupported));
                             } else {
-                                try!(self.do_ssl_request());
+                                try!(self.do_ssl_request(&handshake));
                                 try!(self.switch_to_ssl());
                             }
                         }
@@ -1192,14 +1192,14 @@ impl Conn {
     }
 
     #[cfg(feature = "ssl")]
-    fn do_ssl_request(&mut self) -> MyResult<()> {
+    fn do_ssl_request(&mut self, hp: &HandshakePacket) -> MyResult<()> {
         let client_flags = self.get_client_flags();
         let mut buf = [0; 4 + 4 + 1 + 23];
         {
             let mut writer = &mut buf[..];
             try!(writer.write_u32::<LE>(client_flags.bits()));
             try!(writer.write_all(&[0u8; 4]));
-            try!(writer.write_u8(consts::UTF8_GENERAL_CI));
+            try!(writer.write_u8(hp.get_default_collation()));
             try!(writer.write_all(&[0u8; 23]));
         }
         self.write_packet(&buf[..])
@@ -1224,7 +1224,7 @@ impl Conn {
             let mut writer = &mut *buf;
             try!(writer.write_u32::<LE>(client_flags.bits()));
             try!(writer.write_all(&[0u8; 4]));
-            try!(writer.write_u8(consts::UTF8_GENERAL_CI));
+            try!(writer.write_u8(hp.get_default_collation()));
             try!(writer.write_all(&[0u8; 23]));
             if let &Some(ref user) = self.opts.get_user() {
                 try!(writer.write_all(user.as_bytes()));
diff --git a/src/consts.rs b/src/consts.rs
index 0fb3eda3..33f6f9b6 100644
--- a/src/consts.rs
+++ b/src/consts.rs
@@ -3,6 +3,7 @@ pub static MAX_PACKET_LEN: usize = 16777219;
 pub static MAX_PAYLOAD_LEN: usize = 16777215;
 
 pub static UTF8_GENERAL_CI: u8 = 33u8;
+pub static UTF8MB4_GENERAL_CI: u8 = 45u8;
 
 /// Server status flags
 bitflags! {
diff --git a/src/packet.rs b/src/packet.rs
index d5043492..e0f54c67 100644
--- a/src/packet.rs
+++ b/src/packet.rs
@@ -187,6 +187,16 @@ impl HandshakePacket {
                          capability_flags: capability_flags, character_set: character_set,
                          status_flags: status_flags, auth_plugin_name: auth_plugin_name})
     }
+    pub fn get_default_collation(&self) -> u8 {
+        // MySQL version 5.5.3 introduced "utf8mb4"
+        if self.server_version >= (5, 5, 3) {
+            // This is a true UTF-8 collation, which supports the full range of code-points
+            consts::UTF8MB4_GENERAL_CI
+        } else {
+            // Fall-back to the "utf8" collation which only supports up to 3-byte code-points
+            consts::UTF8_GENERAL_CI
+        }
+    }
 }
 
 #[cfg(test)]


Test patch: