diff --git a/src/Libraries/Interceptor.cs b/src/Libraries/Interceptor.cs new file mode 100644 index 0000000..5316c3a --- /dev/null +++ b/src/Libraries/Interceptor.cs @@ -0,0 +1,27 @@ +using System; +using System.Data; +using MySql.Data.MySqlClient; + +namespace CommandApp +{ + /// + /// Filters out collations with NULL id (e.g. UCA-14.0.0) from SHOW COLLATION command + /// Credit to Jeffraska + /// https://github.com/jeffraska/Jf.MySql.Data.Collations + /// + public sealed class Interceptor : BaseCommandInterceptor + { + public override bool ExecuteReader(string sql, CommandBehavior behavior, ref MySqlDataReader returnValue) + { + if (!sql.Equals("SHOW COLLATION", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + using var command = ActiveConnection.CreateCommand(); + command.CommandText = "SHOW COLLATION WHERE id IS NOT NULL"; + returnValue = command.ExecuteReader(behavior); + return true; + } + } +} diff --git a/src/Libraries/MySql.cs b/src/Libraries/MySql.cs index 0ab8f56..52fe4eb 100644 --- a/src/Libraries/MySql.cs +++ b/src/Libraries/MySql.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Data; using System.Threading; +using CommandApp; namespace Oxide.Core.MySql.Libraries { @@ -225,6 +226,8 @@ public Connection OpenDb(string host, int port, string database, string user, st public Connection OpenDb(string conStr, Plugin plugin, bool persistent = false) { + Utf8mb3.Enable(); + conStr += ";commandinterceptors=CommandApp.Interceptor," + typeof(CommandApp.Interceptor).Assembly.FullName; Dictionary connections; if (!_connections.TryGetValue(plugin?.Name ?? "null", out connections)) { diff --git a/src/Libraries/Utf8mb3.cs b/src/Libraries/Utf8mb3.cs new file mode 100644 index 0000000..e106751 --- /dev/null +++ b/src/Libraries/Utf8mb3.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; +using MySql.Data.MySqlClient; + +namespace CommandApp +{ + // ReSharper disable once InconsistentNaming + /// Credit to Jeffraska + /// https://github.com/jeffraska/Jf.MySql.Data.Collations + /// + public static class Utf8mb3 + { + // verified that starting version 6.10.0, CharSetMap.mapping was changed to CharSetMap._mapping + // code will still work if MySql.Data.dll is upgraded + private static readonly Version NewFieldNamingVersion = new Version(6, 10, 0); + + public static void Enable() + { + // Add internal mapping of database utf8mb3 charset to .NET framework's UTF-8 encoding + var assembly = System.Reflection.Assembly.GetAssembly(typeof(MySqlConnection)); + var connectorVersion = assembly.GetName().Version; + + var mappingFieldName = connectorVersion >= NewFieldNamingVersion ? "_mapping" : "mapping"; + + var mappingField = assembly + .GetType("MySql.Data.MySqlClient.CharSetMap").GetField(mappingFieldName, + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | + System.Reflection.BindingFlags.Static); + + if (mappingField != null) + { + var mappingDictionary = (IDictionary)mappingField.GetValue(null); + var utf8Mapping = mappingDictionary["utf8"]; + + if (utf8Mapping != null) + { + try + { + mappingDictionary.Add("utf8mb3", utf8Mapping); + } + catch (ArgumentException) + { + // Item already exist + } + } + } + } + } +} diff --git a/src/Oxide.MySql.csproj b/src/Oxide.MySql.csproj index 6fa230b..472066b 100644 --- a/src/Oxide.MySql.csproj +++ b/src/Oxide.MySql.csproj @@ -16,6 +16,9 @@ True NU1701 + + 9.0 +