From b280b87de2d7fdb57d0a2d12ee9ec06b34a5d0bc Mon Sep 17 00:00:00 2001 From: Hiroki Noda Date: Sat, 24 Oct 2020 07:11:15 +0900 Subject: [PATCH 1/2] Use utf8mb4 by default This commit changes default collation as "utf8mb4_generic_ci" which is available from MySQL 5.5.3. --- source/mysql/protocol/comms.d | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/source/mysql/protocol/comms.d b/source/mysql/protocol/comms.d index 94dfc9df..32ce51cf 100644 --- a/source/mysql/protocol/comms.d +++ b/source/mysql/protocol/comms.d @@ -25,6 +25,8 @@ import std.conv; import std.digest.sha; import std.exception; import std.range; +import std.regex; +import std.typecons; import std.variant; import mysql.connection; @@ -810,7 +812,7 @@ body // Request a conventional maximum packet length. 1.packInto(packet[8..12]); - packet ~= 33; // Set UTF-8 as default charSet + packet ~= getDefaultCollation(conn._serverVersion); // There's a statutory block of zero bytes here - fill them in. foreach(i; 0 .. 23) @@ -1112,3 +1114,17 @@ package(mysql) void enableMultiStatements(Connection conn, bool on) auto packet = conn.getPacket(); enforce!MYXProtocol(packet[0] == 254 && packet.length == 5, "Unexpected response to SET_OPTION command"); } + +private ubyte getDefaultCollation(string serverVersion) +{ + static re = ctRegex!`^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)`; + auto captured = serverVersion.matchFirst(re); + auto major = captured[1].to!ushort; + auto minor = captured[2].to!ushort; + auto patch = captured[3].to!ushort; + auto mysqlServerVersion = tuple(major, minor, patch); + + if (mysqlServerVersion >= tuple(5,5,3)) // MySQL >= 5.5.3 supports utf8mb4 + return 45; // Set utf8mb4_general_ci as default + return 33; // Set utf8_general_ci as default +} From c2b2f26ff0f1402fc431070d0fc781a9a45fefc6 Mon Sep 17 00:00:00 2001 From: Hiroki Noda Date: Wed, 26 May 2021 01:16:30 +0900 Subject: [PATCH 2/2] Stop using regex and add test --- source/mysql/protocol/comms.d | 46 ++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/source/mysql/protocol/comms.d b/source/mysql/protocol/comms.d index 32ce51cf..a1e28997 100644 --- a/source/mysql/protocol/comms.d +++ b/source/mysql/protocol/comms.d @@ -21,12 +21,11 @@ Next tasks for this sub-package's cleanup: module mysql.protocol.comms; import std.algorithm; +import std.array; import std.conv; import std.digest.sha; import std.exception; import std.range; -import std.regex; -import std.typecons; import std.variant; import mysql.connection; @@ -47,7 +46,7 @@ package struct ProtocolPrepared import std.datetime; import std.variant; import mysql.types; - + static ubyte[] makeBitmap(in Variant[] inParams) { size_t bml = (inParams.length+7)/8; @@ -453,7 +452,7 @@ package struct ProtocolPrepared Variant[] inParams, ParameterSpecialization[] psa) { conn.autoPurge(); - + ubyte[] packet; conn.resetPacket(); @@ -773,7 +772,7 @@ body } conn.autoPurge(); - + conn.resetPacket(); ubyte[] header; @@ -969,7 +968,7 @@ package(mysql) SvrCapFlags setClientFlags(SvrCapFlags serverCaps, SvrCapFlags ca // didn't supply it cCaps |= SvrCapFlags.PROTOCOL41; cCaps |= SvrCapFlags.SECURE_CONNECTION; - + return cCaps; } @@ -1000,7 +999,7 @@ package(mysql) PreparedServerInfo performRegister(Connection conn, const(char[]) scope(failure) conn.kill(); PreparedServerInfo info; - + conn.sendCmd(CommandType.STMT_PREPARE, sql); conn._fieldCount = 0; @@ -1117,14 +1116,27 @@ package(mysql) void enableMultiStatements(Connection conn, bool on) private ubyte getDefaultCollation(string serverVersion) { - static re = ctRegex!`^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)`; - auto captured = serverVersion.matchFirst(re); - auto major = captured[1].to!ushort; - auto minor = captured[2].to!ushort; - auto patch = captured[3].to!ushort; - auto mysqlServerVersion = tuple(major, minor, patch); - - if (mysqlServerVersion >= tuple(5,5,3)) // MySQL >= 5.5.3 supports utf8mb4 - return 45; // Set utf8mb4_general_ci as default - return 33; // Set utf8_general_ci as default + // MySQL >= 5.5.3 supports utf8mb4 + const v = serverVersion + .splitter('.') + .map!(a => a.parse!ushort) + .array; + + if (v[0] < 5) + return 33; // Set utf8_general_ci as default + if (v[1] < 5) + return 33; // Set utf8_general_ci as default + if (v[2] < 3) + return 33; // Set utf8_general_ci as default + + return 45; // Set utf8mb4_general_ci as default +} + +unittest +{ + assert(getDefaultCollation("5.5.3") == 45); + assert(getDefaultCollation("5.5.2") == 33); + + // MariaDB: https://mariadb.com/kb/en/connection/#initial-handshake-packet + assert(getDefaultCollation("5.5.5-10.0.7-MariaDB") == 45); }